The first thing that *leaped* to my eye as that you're using `List<string>` for huge strings and calling `Contains` on them. This is very bad for performance, because `List<>`'s search is an O(n) operation - to check if an item exists, it has to go linearly through the entire collection until it's found. The data structure you want to be using is `HashSet<string>`, where checking for the existence of a given string is an O(1) operation, on average. It makes all set operations faster. Note that it can accept a StringComparer object to make it case insensitive, which saves you having to call `ToLower` on every words, which also slows you down - each ToLower call creates a new String object in memory, which, for large books, will cause a lot of memory pressure. So the first part of your method can be expressed this way: public static void LongestPhrase(string Book1, string Book2, ref string Phrase, ref int WIndex1, ref int WIndex2) { string[] Words1 = Book1.Split('-'); string[] Words2 = Book2.Split('-'); // load Book1 HashSet<string> uniqueRepeatedWords = new HashSet<string>(Words1, StringComparer.CurrentCultureIgnoreCase); // keep only those in Book2 too. uniqueRepeatedWords.IntersectWith(Words2); // Find positions. // Notice I use a case-insensitive comparer instead of constant ToLower, // and save the current word once to a local var instead of the // noisier and less clear array access every time. // Additionally, instead of having the same line in both if and else, // I just create the new List<int> if it doesn't exist, and // go back to the same incrementing code after. var positionsInBook1 = new Dictionary<string, List<int>>(StringComparer.CurrentCultureIgnoreCase); for (int i = 0; i < Words1.Length; i++) { var word = Words1[i]; if (uniqueRepeatedWords.Contains(word)) { if (!positionsInBook1 .ContainsKey(word)) { positionsInBook1.Add(word, new List<int>()); } positionsInBook1[word].Add(i); } } // Now do the same find-position code for Book2 - ideally, move // the code to different method and call it twice, with different // parameters. var positionsInBook2 = FindWordPositions(Words2, uniqueRepeatedWords); **General notes** * Your variable naming conventions are confusing. It's customary to name local variables in lowercase (`words1`, not `Words1`), and it's *very* confusing to have variables called `Words1` and `Word1`, both of which being *lists* of words. I would name them more explicitly - `allWordsInBook1`, for instance. Similarly, `Inf` and `Infs` are almost identical and very confusion. `posInBook1` and `posInBook2` might be clearer. * I'd suggest not defining your variables at the top of the method, but closer to where you use them. In your code, when you start using `Index1`, for instance, you have to scroll back a page to remember what it is. * You're not actually *incrementing* `Counter1` and `Counter3` anywhere, are you? They're always identical to `Inf` and `Infs` respectively? In that case, they're just adding visual noise and cognitive weight. `Start1/Start2` should be named `counter1/counter2`, since they're the ones that are incremented. * Again, every time you find yourself check for equality with `ToLower`, replace it with `string1.Equals(string2, StringComparison.CurrentCultureIgnoreCase)`. When dealing with a huge number of strings inside nested loops, this can have a real effect on memory usage.