Home > Net >  Fun With Anagrams
Fun With Anagrams

Time:05-21

Hi im trying to solve this riddle:

Given an array of strings, remove each string that is an anagram of an earlier string, then return the remaining array in sorted order.

Example str = ['code', 'doce', 'ecod', 'framer', 'frame']

code and doce are anagrams. Remove doce from the array and keep the first occurrence code in the array. code and ecod are anagrams. Remove ecod from the array and keep the first occurrence code in the array. code and framer are not anagrams. Keep both strings in the array. framer and frame are not anagrams due to the extra r in framer. Keep both strings in the array. Order the remaining strings in ascending order: ['code','frame','framer'].

This is what i got so far:

  static void Main(string[] args)
        {
            List<string> str = new List<string>();
            str.Add("code");
            str.Add("doce");
            str.Add("ecod");
            str.Add("framer");
            str.Add("frame");

            foreach (var item in funWithAnagrams(str))
            {
                Console.WriteLine(item);
            }
          
        }


        public static List<string> funWithAnagrams(List<string> text)
        {
           
            for (int i = 0; i < text.Count; i  )
            {
                for (int j = 1 ; j < text.Count; j  )
                {
                    if (text[i].Length==text[j].Length )
                    {
                        if (isAnagram(text[i],text[j]))
                        {
                            text.RemoveAt(j);
                        }
                    }
                }
            }
            text.Sort();
            return text;
        }

        public static bool isAnagram(string a, string b)
        {
            char[] arr1 = a.ToCharArray();
            Array.Sort(arr1);
            char[] arr2 = b.ToCharArray();
            Array.Sort(arr2);

            string c = new string(arr1); 
            string d = new string(arr2);

            if (c==d)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

And the result is : code and framer. Can you help me?

CodePudding user response:

You have a few issues here but mainly don't remove elements during iteration (this almost always causes bad behavior because you might be skipping over "frame" here). That behavior is actually illegal in foreach statements.

Here's a working version I just made

var anagrams = new List<string>() {
  "code",
  "doce",
  "ecod",
  "framer",
  "frame"
};

var noAnagrams = new List<string>();
var result = new List<string>();

foreach (var a in anagrams) {
  var arr1 = a.ToCharArray();
  Array.Sort(arr1);
  var sorted = new string(arr1);

  if (!noAnagrams.Contains(sorted)) {
    noAnagrams.Add(sorted);
    result.Add(a);
  }
}

In the end result = {code, frame, framer}

I used two different lists here to preserve the correct spelling of the original anagrams. You would have the same values in noAnagrams but they would be the sorted version.

CodePudding user response:

Here's a super obtuse/obfuscated LINQ way of doing it...

Dictionary<int, string> result = new Dictionary<int, string>();

str.Select((s, i) => new { s, i })
    .ToDictionary(x => x.i, x =>
        new string(x.s.ToCharArray()
            .OrderBy(s => s)
                .ToArray()))
                .ToList()
                .ForEach(pair => {
                    if (!result.ContainsValue(pair.Value))
                    {
                        result.Add(pair.Key, pair.Value);
                        Console.WriteLine(str[pair.Key]);
                    }
                });

CodePudding user response:

When removing while looping you should either remove or loop:

for (int i = 0; i < text.Count; i  )
  for (int j = i   1 ; j < text.Count; ) // note abscence of   j
    if (text[i].Length == text[j].Length && isAnagram(text[i],text[j])
      text.RemoveAt(j); // either remove
    else
      j  ;              // or go to the next item

text.Sort();

A better approach is sorting: sort letters within words and compare:

odce => cdeo
code => cdeo

Code:

public static List<string> funWithAnagrams(List<string> text) {
  if (text is null)
    throw new ArgumentNullException(nameof(text));

  var unique = new HashSet<string>();

  for (int i = 0; i < text.Count; ) // no i   here
    if (unique.Add(string.Concat(text[i].OrderBy(c => c))))
      i  = 1;           // either go to the next word
    else
      text.RemoveAt(i); // or remove current word

  text.Sort();

  return text;
}

Please, fiddle

  • Related