Always specify access modifiers,
Follow the .Net naming guidelines, methods and namespaces are PascalCase
RobH beat me to the rest of the usual cosmetic stuff.
When it comes to performance there may be a lot to gain from constructing a map from [FirstCharacter, LastCharacter] -> CandidateWord. Constructing such a mapping is not free but if you parse multiple inputs it will be worth it.
You can construct the map using Linq:
var words = File.ReadAllLines(path);
var wordLookup = words.Where(word => word.Length >= 5)
.GroupBy(word => word[0])
.ToDictionary(group => group.Key, group => group.ToLookup(w => w[w.Length - 1]));
And then you can find candidates by using a helper:
private static IEnumerable<string> GetCandidateWords(string input, Dictionary<char, ILookup<char, string>> candidateLookup)
{
var firstChar = input[0];
var lastChar = input[input.Length - 1];
var maxLength = input.Length;
return candidateLookup[firstChar][lastChar].Where(w => w.Length <= maxLength);
}
And then you filter out the valid words:
public static IEnumerable<string> GetValidWords(this string input, Dictionary<char, ILookup<char, string>> candidateLookup)
{
var candidateWords = GetCandidateWords(input, candidateLookup);
return candidateWords.Where(candidate => ValidateCandidateWord(input, candidate));
}
And your check now renamed to be more explicit about it's function:
private static bool ValidateCandidateWord(string input, string word)
{
int searchFrom = 0;
foreach(var c in word)
{
var currentIndex = input.IndexOf(c, searchFrom);
if(currentIndex == -1)
{
return false;
}
searchFrom = currentIndex + 1;
}
return true;
}
I changed it to search from the next character on, this means queen is no longer a valid option from qwertyuytresdftyuioknn as there is only a single e after the first u. I can't tell which way is correct from the spec so this is based on opinion.
If you put all the methods in a static class you end up with an extension method on string:
public static class StringSwipeExtentions
{
private static IEnumerable<string> GetCandidateWords(string input, Dictionary<char, ILookup<char, string>> candidateLookup)
{
var firstChar = input[0];
var lastChar = input[input.Length - 1];
var maxLength = input.Length;
return candidateLookup[firstChar][lastChar].Where(w => w.Length <= maxLength);
}
public static IEnumerable<string> GetValidWords(this string input, Dictionary<char, ILookup<char, string>> candidateLookup)
{
var candidateWords = GetCandidateWords(input, candidateLookup);
return candidateWords.Where(candidate => ValidateCandidateWord(input, candidate));
}
private static bool ValidateCandidateWord(string input, string word)
{
int searchFrom = 0;
foreach(var c in word)
{
var currentIndex = input.IndexOf(c, searchFrom);
if(currentIndex == -1)
{
return false;
}
searchFrom = currentIndex + 1;
}
return true;
}
}
And you using it:
public static void Main()
{
var inputs = new[]
{
"qwertyuytresdftyuioknn",
"gijakjthoijerjidsdfnokg",
"cghhjkkllooiuytrrdfdftgyuiuytrfdsaazzseertyuioppoiuhgfcxxcfvghujiiuytrfddeews"
};
var words = File.ReadAllLines(path);
var wordLookup = words.Where(word => word.Length >= 5)
.GroupBy(word => word[0])
.ToDictionary(group => group.Key, group => group.ToLookup(w => w[w.Length - 1]));
foreach(var input in inputs)
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
var validWords = input.GetValidWords(wordLookup);
stopwatch.Stop();
Console.WriteLine("{0} done in {1}ms, valid words:", input, stopwatch.ElapsedMilliseconds);
foreach(var validWord in validWords)
{
Console.WriteLine(validWord);
}
}
}