If you want to find words whose letters and frequency thereof are restricted by the given phrase,
you can construct a regex to do this for you:
sentence = "Ziegler's Giant Bar"
# count how many times each letter occurs in the
# sentence (ignoring case, and removing non-letters)
counts = Hash.new(0)
sentence.downcase.gsub(/[^a-z]/,'').split(//).each do |letter|
counts[letter] += 1
end
letters = counts.keys.join
length = counts.values.inject { |a,b| a + b }
# construct a regex that matches upto that many occurences
# of only those letters, ignoring non-letters
# (in a positive look ahead)
length_regex = /(?=^(?:[^a-z]*[#{letters}]){1,#{length}}[^a-z]*$)/i
# construct regexes that matches each letter up to its
# proper frequency (in a positive look ahead)
count_regexes = counts.map do |letter, count|
/(?=^(?:[^#{letter}]*#{letter}){0,#{count}}[^#{letter}]*$)/i
end
# combine the regexes, to form a regex that will only
# match words that are made of a subset of the letters in the string
regex = /#{length_regex}#{count_regexes.join('')}/
# open a big file of words, and find all the ones that match
words = File.open("/usr/share/dict/words") do |f|
f.map { |word| word.chomp }.find_all { |word| regex =~ word }
end
words.length #=> 3182
words #=> ["A", "a", "aa", "aal", "aalii", "Aani", "Ab", "aba", "abaiser", "Abantes",
"Abaris", "abas", "abase", "abaser", "Abasgi", "abate", "abater", "abatis",
...
"ba", "baa", "Baal", "baal", "Baalist", "Baalite", "Baalize", "baar", "bae",
"Baeria", "baetzner", "bag", "baga", "bagani", "bagatine", "bagel", "bagganet",
...
"eager", "eagle", "eaglet", "eagre", "ean", "ear", "earing", "earl", "earlet",
"earn", "earner", "earnest", "earring", "eartab", "ease", "easel", "easer",
...
"gab", "Gabe", "gabi", "gable", "gablet", "Gabriel", "Gael", "gaen", "gaet",
"gag", "gagate", "gage", "gageable", "gagee", "gageite", "gager", "Gaia",
...
"Iberian", "Iberis", "iberite", "ibis", "Ibsenite", "ie", "Ierne", "Igara",
"Igbira", "ignatia", "ignite", "igniter", "Ila", "ilesite", "ilia", "Ilian",
...
"laang", "lab", "Laban", "labia", "labiate", "labis", "labra", "labret", "laet",
"laeti", "lag", "lagan", "lagen", "lagena", "lager", "laggar", "laggen",
...
"Nabal", "Nabalite", "nabla", "nable", "nabs", "nae", "naegate", "naegates",
"nael", "nag", "Naga", "naga", "Nagari", "nagger", "naggle", "nagster", "Naias",
...
"Rab", "rab", "rabat", "rabatine", "Rabi", "rabies", "rabinet", "rag", "raga",
"rage", "rager", "raggee", "ragger", "raggil", "raggle", "raging", "raglan",
...
"sa", "saa", "Saan", "sab", "Saba", "Sabal", "Saban", "sabe", "saber",
"saberleg", "Sabia", "Sabian", "Sabina", "sabina", "Sabine", "sabine", "Sabir",
...
"tabes", "Tabira", "tabla", "table", "tabler", "tables", "tabling", "Tabriz",
"tae", "tael", "taen", "taenia", "taenial", "tag", "Tagabilis", "Tagal",
...
"zest", "zeta", "ziara", "ziarat", "zibeline", "zibet", "ziega", "zieger",
"zig", "zing", "zingel", "Zingiber", "zira", "zirai", "Zirbanit", "Zirian"]
Positive lookaheads let you make a regex that matches a position in the string where some specified pattern matches without consuming the part of the string that matches.
We use them here to match the same string against multiple patterns in a single regex.
The position only matches if all our patterns match.
If we allow infinite reuse of letters from the original phrase (like Knuth did according to glenra's comment), then it's even easier to construct a regex:
sentence = "Ziegler's Giant Bar"
# find all the letters in the sentence
letters = sentence.downcase.gsub(/[^a-z]/,'').split(//).uniq
# construct a regex that matches any line in which
# the only letters used are the ones in the sentence
regex = /^([^a-z]|[#{letters.join}])*$/i
# open a big file of words, and find all the ones that match
words = File.open("/usr/share/dict/words") do |f|
f.map { |word| word.chomp }.find_all { |word| regex =~ word }
end
words.length #=> 6725
words #=> ["A", "a", "aa", "aal", "aalii", "Aani", "Ab", "aba", "abaiser", "abalienate",
...
"azine", "B", "b", "ba", "baa", "Baal", "baal", "Baalist", "Baalite",
"Baalize", "baar", "Bab", "baba", "babai", "Babbie", "Babbitt", "babbitt",
...
"Britannian", "britten", "brittle", "brittleness", "brittling", "Briza",
"brizz", "E", "e", "ea", "eager", "eagerness", "eagle", "eagless", "eaglet",
"eagre", "ean", "ear", "earing", "earl", "earless", "earlet", "earliness",
...
"eternalize", "eternalness", "eternize", "etesian", "etna", "Etnean", "Etta",
"Ettarre", "ettle", "ezba", "Ezra", "G", "g", "Ga", "ga", "gab", "gabber",
"gabble", "gabbler", "Gabe", "gabelle", "gabeller", "gabgab", "gabi", "gable",
...
"grittiness", "grittle", "Grizel", "Grizzel", "grizzle", "grizzler", "grr",
"I", "i", "iba", "Iban", "Ibanag", "Iberes", "Iberi", "Iberia", "Iberian",
...
"itinerarian", "itinerate", "its", "Itza", "Izar", "izar", "izle", "iztle",
"L", "l", "la", "laager", "laang", "lab", "Laban", "labara", "labba", "labber",
...
"litter", "litterer", "little", "littleness", "littling", "littress", "litz",
"Liz", "Lizzie", "Llanberisslate", "N", "n", "na", "naa", "Naassenes", "nab",
"Nabal", "Nabalite", "Nabataean", "Nabatean", "nabber", "nabla", "nable",
...
"niter", "nitraniline", "nitrate", "nitratine", "Nitrian", "nitrile",
"nitrite", "nitter", "R", "r", "ra", "Rab", "rab", "rabanna", "rabat",
"rabatine", "rabatte", "rabbanist", "rabbanite", "rabbet", "rabbeting",
...
"riteless", "ritelessness", "ritling", "rittingerite", "rizzar", "rizzle", "S",
"s", "sa", "saa", "Saan", "sab", "Saba", "Sabaean", "sabaigrass", "Sabaist",
...
"strigine", "string", "stringene", "stringent", "stringentness", "stringer",
"stringiness", "stringing", "stringless", "strit", "T", "t", "ta", "taa",
"Taal", "taar", "Tab", "tab", "tabaret", "tabbarea", "tabber", "tabbinet",
...
"tsessebe", "tsetse", "tsia", "tsine", "tst", "tzaritza", "Tzental", "Z", "z",
"za", "Zabaean", "zabeta", "Zabian", "zabra", "zabti", "zabtie", "zag", "zain",
...
"Zirian", "Zirianian", "Zizania", "Zizia", "zizz"]