Here is my solution.
It tries to generate unambiguous abbrevations, if those don't exist,
it uses the least ambiguous one and always avoids using the same
abbrevation twice.
There's also a readability thing built in, strings with many
characters at the beginning or having the characters split equally
over the beginning and ending parts are considered the most readable.

class String
  def compress(total_length, end_length)
    self[0...total_length-end_length] + '...' + self[length-end_length..-1]
  end
end

class Array
  def compress!(max_length)
    max_length = 4 if max_length < 4
	score = Hash.new(0)
	usable_length = max_length - 3
	order = (0..usable_length).sort_by{|len|
[(len-usable_length.to_f/2).abs,len].min}
	to_compress = select {|s| s.length > usable_length}
	to_compress.each {|s| order.map{|l| score[s.compress(usable_length,l)] += 1 } }
	to_compress.each{|s|
	  s.replace order.map{|l| s.compress(usable_length,l) }.min{|a,b|
score[a] <=> score[b]}
	  score[s] += 100
	}
	self
  end
end

if __FILE__==$0
  p ['users_controller', 'users_controller_test','account_controller',
'account_controller_test','bacon'].compress!(10)
  p Array.new(10){'abcdefghijklmnopqrstuvwxyz'}.compress!(12)
  p ['aaaaaazbbbbb','aaaaaaybbbbb'].compress!(9)
end