Используя Python, найдите анаграммы для списка слов
Если у меня есть список строк, например:
["car", "tree", "boy", "girl", "arc"...]
что мне делать, чтобы найти анаграммы в этом списке? Например (car, arc)
.
Я попытался использовать цикл for для каждой строки, и я использовал if
для того, чтобы игнорировать строки разной длины, но я не могу получить правильный результат.
как я могу просмотреть каждую букву в строке и сравнить ее с другими в списке в другом порядке?
Я читал несколько подобных вопросов, но ответы были слишком продвинутый. Я ничего не могу импортировать, и я могу использовать только основные функции.
17 ответов
чтобы сделать это для 2 строк, вы можете сделать это:
def isAnagram(str1, str2):
str1_list = list(str1)
str1_list.sort()
str2_list = list(str2)
str2_list.sort()
return (str1_list == str2_list)
что касается итерации в списке, она довольно прямолинейна
создать словарь (отсортированное слово, список слов). Все слова, которые находятся в одном списке, являются анаграммами друг друга.
from collections import defaultdict
def load_words(filename='/usr/share/dict/american-english'):
with open(filename) as f:
for word in f:
yield word.rstrip()
def get_anagrams(source):
d = defaultdict(list)
for word in source:
key = "".join(sorted(word))
d[key].append(word)
return d
def print_anagrams(word_source):
d = get_anagrams(word_source)
for key, anagrams in d.iteritems():
if len(anagrams) > 1:
print(key, anagrams)
word_source = load_words()
print_anagrams(word_source)
или:
word_source = ["car", "tree", "boy", "girl", "arc"]
print_anagrams(word_source)
одним из решений является сортировка слова, которое вы ищете анаграммы (например, с помощью sorted
), отсортируйте альтернативу и сравните их.
Итак, если вы будете искать анаграммы "rac" в списке ['car', 'girl', 'tofu', 'rca']
, ваш код может выглядеть так:
word = sorted('rac')
alternatives = ['car', 'girl', 'tofu', 'rca']
for alt in alternatives:
if word == sorted(alt):
print alt
Сортировать каждый элемент, а затем искать дубликаты. Существует встроенная функция сортировки, поэтому вам не нужно ничего импортировать
есть несколько решений этой проблемы:
-
классический подход
во-первых, давайте рассмотрим, что определяет анаграмма: два слова являются анаграммами друг друга, если они состоят из одного и того же набора букв, и каждая буква появляется точно такое же число или время в обоих словах. Это в основном гистограмма количества букв каждого слова. Это идеальный вариант использования для
collections.Counter
структуры данных (см. docs). Алгоритмы следующие:- построить словарь, где ключи будут гистограммы и значения будут списки слов, которые имеют эту гистограмму.
- для каждого слова построить его гистограмму и добавить его в список, который соответствует этой гистограммы.
- выходной список значений словаря.
вот код:
from collections import Counter, defaultdict def anagram(words): anagrams = defaultdict(list) for word in words: histogram = tuple(Counter(word).items()) # build a hashable histogram anagrams[histogram].append(word) return list(anagrams.values()) keywords = ("hi", "hello", "bye", "helol", "abc", "cab", "bac", "silenced", "licensed", "declines") print(anagram(keywords))
обратите внимание, что построение
Counter
иO(l)
, сортируя каждое словоO(n*log(l))
где L-длина слова. -
решение анаграмм с использованием простых чисел
Это более продвинутое решение, которое опирается на" мультипликативную единственность " простых чисел. Вы можете обратиться к этому сообщению SO: сравнение анаграмм с использованием простых чисел и вот пример реализации python.
def findanagranfromlistofwords(li):
dict = {}
index=0
for i in range(0,len(li)):
originalfirst = li[index]
sortedfirst = ''.join(sorted(str(li[index])))
for j in range(index+1,len(li)):
next = ''.join(sorted(str(li[j])))
print next
if sortedfirst == next:
dict.update({originalfirst:li[j]})
print "dict = ",dict
index+=1
print dict
findanagranfromlistofwords(["car", "tree", "boy", "girl", "arc"])
решение в python может быть следующим:
class Word:
def __init__(self, data, index):
self.data = data
self.index = index
def printAnagrams(arr):
dupArray = []
size = len(arr)
for i in range(size):
dupArray.append(Word(arr[i], i))
for i in range(size):
dupArray[i].data = ''.join(sorted(dupArray[i].data))
dupArray = sorted(dupArray, key=lambda x: x.data)
for i in range(size):
print arr[dupArray[i].index]
def main():
arr = ["dog", "act", "cat", "god", "tac"]
printAnagrams(arr)
if __name__== '__main__':
main()
- сначала создайте дубликат списка тех же слов с индексами, представляющими их индексы позиций.
- затем отсортируйте отдельные строки дублирующего списка
- затем отсортируйте сам дубликат списка на основе строк.
- наконец, распечатайте исходный список с индексами, используемыми из дублирующего массива.
временная сложность выше O (NMLogN + NMLogM) = O (NMlogN)
Я использую словарь для хранения каждого символа строки по одному. Затем повторите вторую строку и найдите символ в словаре, если он присутствует, уменьшите количество соответствующего ключа из словаря.
class Anagram:
dict = {}
def __init__(self):
Anagram.dict = {}
def is_anagram(self,s1, s2):
print '***** starting *****'
print '***** convert input strings to lowercase'
s1 = s1.lower()
s2 = s2.lower()
for i in s1:
if i not in Anagram.dict:
Anagram.dict[i] = 1
else:
Anagram.dict[i] += 1
print Anagram.dict
for i in s2:
if i not in Anagram.dict:
return false
else:
Anagram.dict[i] -= 1
print Anagram.dict
for i in Anagram.dict.keys():
if Anagram.dict.get(i) == 0:
del Anagram.dict[i]
if len(Anagram.dict) == 0:
print Anagram.dict
return True
else:
return False
import collections
def find_anagrams(x):
anagrams = [''.join(sorted(list(i))) for i in x]
anagrams_counts = [item for item, count in collections.Counter(anagrams).items() if count > 1]
return [i for i in x if ''.join(sorted(list(i))) in anagrams_counts]
простое решение в Python:
def anagram(s1,s2):
# Remove spaces and lowercase letters
s1 = s1.replace(' ','').lower()
s2 = s2.replace(' ','').lower()
# Return sorted match.
return sorted(s1) == sorted(s2)
этот поможет вам:
предполагая, что ввод задается как строки, разделенные запятыми
вход консоли : abc,bac,car,rac,pqr,acb,acr, abc
in_list = list()
in_list = map(str, raw_input("Enter strings seperated by comma").split(','))
list_anagram = list()
for i in range(0, len(in_list) - 1):
if sorted(in_list[i]) not in list_anagram:
for j in range(i + 1, len(in_list)):
isanagram = (sorted(in_list[i]) == sorted(in_list[j]))
if isanagram:
list_anagram.append(sorted(in_list[i]))
print in_list[i], 'isanagram'
break
набор является соответствующей структурой данных для вывода, так как вы, по-видимому, не хотите избыточности в выходе. Словарь идеально подходит для поиска, если определенная последовательность букв была ранее замечена и от какого слова она исходила. Использование того факта, что мы можем добавить один и тот же элемент в набор несколько раз, не расширяя набор, позволяет нам уйти с одним циклом for.
def return_anagrams(word_list):
d = {}
out = set()
for word in word_list:
s = ''.join(sorted(word))
try:
out.add(d[s])
out.add(word)
except:
d[s] = word
return out
быстрый способ сделать это коммутативно свойство сложения:
import numpy as np
def vector_anagram(l):
d, out = dict(), set()
for word in l:
s = np.zeros(26, dtype=int)
for c in word:
s[ord(c)-97] += 1
s = tuple(s)
try:
out.add(d[s])
out.add(word)
except:
d[s] = word
return out
- вычислить длину каждого слова.
- вычислить каждое слово ascii сумма символов.
- сортируйте символы каждого слова по их значениям ascii и установите упорядоченное слово.
- групповые слова в зависимости от их длины.
- для каждой группы перегруппируйте список в соответствии с их суммой символов ascii.
- для каждого небольшого списка проверьте только упорядоченные слова. Если упорядочить слова же эти слова анаграммой.
здесь мы имеем 1000.000 слов список. 1000.000 слова
namespace WindowsFormsApplication2
{
public class WordDef
{
public string Word { get; set; }
public int WordSum { get; set; }
public int Length { get; set; }
public string AnagramWord { get; set; }
public string Ordered { get; set; }
public int GetAsciiSum(string word)
{
int sum = 0;
foreach (char c in word)
{
sum += (int)c;
}
return sum;
}
}
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication2
{
public partial class AngramTestForm : Form
{
private ConcurrentBag<string> m_Words;
private ConcurrentBag<string> m_CacheWords;
private ConcurrentBag<WordDef> m_Anagramlist;
public AngramTestForm()
{
InitializeComponent();
m_CacheWords = new ConcurrentBag<string>();
}
private void button1_Click(object sender, EventArgs e)
{
m_Words = null;
m_Anagramlist = null;
m_Words = new ConcurrentBag<string>();
m_Anagramlist = new ConcurrentBag<WordDef>();
if (m_CacheWords.Count == 0)
{
foreach (var s in GetWords())
{
m_CacheWords.Add(s);
}
}
m_Words = m_CacheWords;
Stopwatch sw = new Stopwatch();
sw.Start();
//DirectCalculation();
AsciiCalculation();
sw.Stop();
Console.WriteLine("The End! {0}", sw.ElapsedMilliseconds);
this.Invoke((MethodInvoker)delegate
{
lbResult.Text = string.Concat(sw.ElapsedMilliseconds.ToString(), " Miliseconds");
});
StringBuilder sb = new StringBuilder();
foreach (var w in m_Anagramlist)
{
if (w != null)
{
sb.Append(string.Concat(w.Word, " - ", w.AnagramWord, Environment.NewLine));
}
}
txResult.Text = sb.ToString();
}
private void DirectCalculation()
{
List<WordDef> wordDef = new List<WordDef>();
foreach (var w in m_Words)
{
WordDef wd = new WordDef();
wd.Word = w;
wd.WordSum = wd.GetAsciiSum(w);
wd.Length = w.Length;
wd.Ordered = String.Concat(w.OrderBy(c => c));
wordDef.Add(wd);
}
foreach (var w in wordDef)
{
foreach (var t in wordDef)
{
if (w.Word != t.Word)
{
if (w.Ordered == t.Ordered)
{
t.AnagramWord = w.Word;
m_Anagramlist.Add(new WordDef() { Word = w.Word, AnagramWord = t.Word });
}
}
}
}
}
private void AsciiCalculation()
{
ConcurrentBag<WordDef> wordDef = new ConcurrentBag<WordDef>();
Parallel.ForEach(m_Words, w =>
{
WordDef wd = new WordDef();
wd.Word = w;
wd.WordSum = wd.GetAsciiSum(w);
wd.Length = w.Length;
wd.Ordered = String.Concat(w.OrderBy(c => c));
wordDef.Add(wd);
});
var tempWordByLength = from w in wordDef
group w by w.Length into newGroup
orderby newGroup.Key
select newGroup;
foreach (var wList in tempWordByLength)
{
List<WordDef> wd = wList.ToList<WordDef>();
var tempWordsBySum = from w in wd
group w by w.WordSum into newGroup
orderby newGroup.Key
select newGroup;
Parallel.ForEach(tempWordsBySum, ws =>
{
List<WordDef> we = ws.ToList<WordDef>();
if (we.Count > 1)
{
CheckCandidates(we);
}
});
}
}
private void CheckCandidates(List<WordDef> we)
{
for (int i = 0; i < we.Count; i++)
{
for (int j = i + 1; j < we.Count; j++)
{
if (we[i].Word != we[j].Word)
{
if (we[i].Ordered == we[j].Ordered)
{
we[j].AnagramWord = we[i].Word;
m_Anagramlist.Add(new WordDef() { Word = we[i].Word, AnagramWord = we[j].Word });
}
}
}
}
}
private static string[] GetWords()
{
string htmlCode = string.Empty;
using (WebClient client = new WebClient())
{
htmlCode = client.DownloadString("https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/10_million_password_list_top_1000000.txt");
}
string[] words = htmlCode.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
return words;
}
}
}
вот впечатляющее решение.
Рег alphabet_count_mapper:
для каждого слова в файл/список
1.создайте словарь алфавитов / символов с начальным числом 0.
2.держите счет всех алфавитов в слове и увеличьте количество в приведенном выше алфавите.
3.создайте алфавит count dict и верните кортеж значений алфавита dict.
функц anagram_counter:
1.создайте словарь с алфавитным кортежем в качестве ключа и подсчетом количества случаев против него.
2.повторите над вышеуказанным dict и если значение > 1, добавьте значение к числу анаграмм.
import sys
words_count_map_dict = {}
fobj = open(sys.argv[1],"r")
words = fobj.read().split('\n')[:-1]
def alphabet_count_mapper(word):
alpha_count_dict = dict(zip('abcdefghijklmnopqrstuvwxyz',[0]*26))
for alpha in word:
if alpha in alpha_count_dict.keys():
alpha_count_dict[alpha] += 1
else:
alpha_count_dict.update(dict(alpha=0))
return tuple(alpha_count_dict.values())
def anagram_counter(words):
anagram_count = 0
for word in words:
temp_mapper = alphabet_count_mapper(word)
if temp_mapper in words_count_map_dict.keys():
words_count_map_dict[temp_mapper] += 1
else:
words_count_map_dict.update({temp_mapper:1})
for val in words_count_map_dict.values():
if val > 1:
anagram_count += val
return anagram_count
print anagram_counter(words)
запустите его с путем файла в качестве аргумента командной строки
вы преобразуете каждый символ в слове в число (по ord () функция), добавьте их для слова. Если два слова имеют одинаковую сумму, то это анаграммы. Затем отфильтруйте суммы, которые встречаются в списке более двух раз.
def sumLet(w):
return sum([ord(c) for c in w])
def find_anagrams(l):
num_l = map(sumLet,l)
return [l[i] for i,num in enumerate(num_l) if num_l.count(num) > 1]
>>> words = ["car", "race", "rac", "ecar", "me", "em"]
>>> anagrams = {}
... for word in words:
... reverse_word=word[::-1]
... if reverse_word in words:
... anagrams[word] = (words.pop(words.index(reverse_word)))
>>> anagrams
20: {'car': 'rac', 'me': 'em', 'race': 'ecar'}
логика:
- начните с первого слова и переверните слово.
- проверьте, что обратное слово присутствует в списке.
- если присутствует, найдите индекс и поп-элемент и сохраните его в словаре, слово как ключ и обратное слово как значение.
Если вы хотите решение на java,
public List<String> findAnagrams(List<String> dictionary) {
// TODO do null check and other basic validations.
Map<String, List<String>> wordMap = new HashMap<String, List<String>>();
for(String word : dictionary) {
// ignore if word is null
char[] tempWord = word.tocharArray();
Arrays.sort(tempWord);
String newWord = new String(tempWord);
if(wordMap.containsKey(newWord)) {
wordMap.put(newWord, wordMap.get(word).add(word));
} else {
wordMap.put(newWord, new ArrayList<>() {word});
}
}
List<String> anagrams = new ArrayList<>();
for(String key : wordMap.keySet()) {
if(wordMap.get(key).size() > 1) {
anagrams.addAll(wordMap.get(key));
}
}
return anagrams;
}