/// <summary> /// given 2 arrays, finds the longest sub array they have in common in o ( (a.Length * log(a.Length) ) time using hash /// </summary> /// <typeparam name="T"></typeparam> /// <param name="a"></param> /// <param name="b"></param> /// <param name="toLong"></param> /// <returns>the longest sub array they have in common</returns> public static List <T> LongestCommonSubArrayWithHash(IList <T> a, IList <T> b, Func <T, long> toLong) { int maxPowerForNormalization = Math.Max(a.Count, b.Count); var hashA = new ArrayHashComputer <T>(a, toLong, maxPowerForNormalization); var hashB = new ArrayHashComputer <T>(b, toLong, maxPowerForNormalization); int minLength = 0; int maxLength = Math.Min(a.Count, b.Count); int firstIndex = -1; while (minLength < maxLength) { var length = (minLength + maxLength + 1) / 2; int firstValidIndex = hashA.FirstIndexOfSameSubArrayWithLength(hashB, length); if (firstValidIndex != -1) { firstIndex = firstValidIndex; minLength = length; } else { maxLength = length - 1; } } //return minLength; //uncomment if we just want the length of the common sub string var result = new List <T>(minLength); for (int i = firstIndex; i < firstIndex + minLength; ++i) { result.Add(a[i]); } return(result); }
/// <summary> /// Find longest palindrome in 'a' in o (a.Length * Log(a.Length) time /// </summary> /// <typeparam name="T"></typeparam> /// <param name="a"></param> /// <param name="toLong"></param> /// <returns> /// Item1: length od the maximum palindrome /// Item2: index of the first palindrome with this length /// </returns> public static Tuple <int, int> LongestPalindrome(List <T> a, Func <T, long> toLong) { if (a.Count <= 1) { return(Tuple.Create(a.Count, 0)); } var aReverse = new List <T>(a); aReverse.Reverse(); var hashA = new ArrayHashComputer <T>(a, toLong, a.Count); var hashReverse = new ArrayHashComputer <T>(aReverse, toLong, a.Count); Func <int, int> palindromeLengthToIndex = (l => IndexFirstPalindromeOfLength(l, hashA, hashReverse)); int maxEvenLengthPalindrome = 2 * Utils.MaximumValidIndex(0, a.Count / 2, halfLength => palindromeLengthToIndex(2 * halfLength) != -1); int maxOddLengthPalindrome = 2 * Utils.MaximumValidIndex(0, (a.Count - 1) / 2, halfLength => palindromeLengthToIndex(2 * halfLength + 1) != -1) + 1; int maxPalindromeLength = Math.Max(maxEvenLengthPalindrome, maxOddLengthPalindrome); var maxIndex = palindromeLengthToIndex(maxPalindromeLength); return(Tuple.Create(maxPalindromeLength, maxIndex)); }
private static int IndexFirstPalindromeOfLength(int l, ArrayHashComputer <T> a, ArrayHashComputer <T> aReversed) { Debug.Assert(a.Length == aReversed.Length); if (l <= 1) { return(0); } for (int start = 0; start < (a.Length - l + 1); ++start) { int end = start + l / 2 - 1; var aHash = a.Hash(start, end); int reverStart = a.Length - (start + l); var aReverseHash = aReversed.Hash(reverStart, reverStart + l / 2 - 1); if (aHash == aReverseHash) { return(start); } } return(-1); }
/// <summary> /// finds the index of first sub array of length 'subArrayLength' that can be found both in 'this and 'other' array in o (this.Length) time /// if 'subArrayLength == 0', it will return 0 (the first occurence of an empty string is at index 0) /// </summary> /// <param name="other"></param> /// <param name="subArrayLength">the exact length of the sub array to find</param> /// <returns>index of the sub array in the 'this' array, or -1 if no such array of length 'subArrayLength' exists</returns> public int FirstIndexOfSameSubArrayWithLength(ArrayHashComputer <T> other, int subArrayLength) { if (subArrayLength <= 0) { return(0); } var hashInOtherArray = new HashSet <long>(); for (var startIndex = 0; startIndex <= other.Length - subArrayLength; ++startIndex) { hashInOtherArray.Add(other.Hash(startIndex, startIndex + subArrayLength - 1)); } for (var startIndex = 0; startIndex <= Length - subArrayLength; ++startIndex) { if (hashInOtherArray.Contains(Hash(startIndex, startIndex + subArrayLength - 1))) { return(startIndex); } } return(-1); //there is no common array of length 'subArrayLength' in 'this' and 'other' array }
/// <summary> /// given 2 strings, finds a smaller string that is the longest substring they have in common (using hash) /// Complexity: o( a.Length * log(a.Length)) ) /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <returns></returns> public static string LongestCommonSubstring_With_Hash(string a, string b) { return(new string(ArrayHashComputer <char> .LongestCommonSubArrayWithHash(a.ToList(), b.ToList(), x => x).ToArray())); }
/// <summary> /// Find longest palindrome in 's' (using hash) /// Complexity: o( a.Length * Log(a.Length) ) /// Memory Complexity: o( a.Length ) /// </summary> /// <returns> /// Item1: length od the maximum palindrome /// Item2: index of the first palindrome with this length /// </returns> public static Tuple <int, int> LongestPalindromeWithHash(string s) { return(ArrayHashComputer <char> .LongestPalindrome(s.ToList(), x => x)); }