查找字符串中重复子串的数量

时间:2017-03-31 19:19:02

标签: string algorithm substring dynamic-programming

我正在寻找一种能够在单个字符串中找到重复子串数的算法。

为此,我一直在寻找一些动态编程算法,但没有找到任何可以帮助我的算法。我只想要一些关于如何做到这一点的教程。

我们说我有一个字符串ABCDABCDABCD。预期的输出为3,因为有ABCD次。{/ p>

对于输入AAAA,输出为4,因为A重复了4次。

对于输入ASDF,输出将为1,因为每个字符仅重复一次。

我希望有人可以指出我正确的方向。谢谢。

3 个答案:

答案 0 :(得分:3)

我采取以下假设:

  • 重复的子字符串必须连续。也就是说,在ABCDABC的情况下,ABC不会被视为重复的子字符串,但在ABCABC的情况下也是如此。
  • 重复的子字符串必须非整体。也就是说,在ABCABC的情况下,ABC不会被视为重复的子字符串。
  • 如果有多个可能的答案,我们希望具有最大值的答案。也就是说,在AAAA的情况下,答案应该是4a是子字符串)而不是2aa是子字符串)。

根据这些假设,算法如下:

  • 将输入字符串表示为inputString
  • 计算输入字符串的KMP failure function数组。将此数组表示为failure[]。如果相对于弦的长度具有线性时间复杂度,则该操作。因此,根据定义,failure[i]表示子串inputString[0....i]的最长正确前缀的长度,该子串也是同一子串的正确后缀。
  • len = inputString.length - failure.lastIndexValue。此时,我们知道如果有任何重复字符串,那么它必须是这个长度len。但我们需要检查一下;首先,只需检查len是否完全划分inputString.length(即inputString.length % len == 0)。如果是,则检查len个字符的每个连续(非重叠)子字符串是否相同;此操作再次具有相对于输入字符串长度的线性时间复杂度。
  • 如果事实证明每个连续的非重叠子字符串相同,则答案为= inputString.length / len。否则,答案只是inputString.length,因为不存在这样的重复子字符串。

总体时间复杂度为O(n),其中n是输入字符串中的字符数。

计算KMP故障数组的示例代码为here

例如,

让输入字符串为abcaabcaabca

它的KMP故障数组是 - [0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8]

所以,我们的len =(12 - 8)= 4。

长度为4的每个连续非重叠子字符串都相同(abca)。
因此答案是12/4 = 3。也就是说,abca重复重复3次。

答案 1 :(得分:1)

使用C#的解决方案是:

 class Program
 {
     public static string CountOfRepeatedSubstring(string str)
     {
         if (str.Length < 2)
         {
             return "-1";
         }

         StringBuilder substr = new StringBuilder();

         // Length of the substring cannot be greater than half of the actual string
         for (int i = 0; i < str.Length / 2; i++)
         {
             // We will iterate through half of the actual string and
             // create a new string by appending the current character to the previous character
             substr.Append(str[i]);

             String clearedOfNewSubstrings = str.Replace(substr.ToString(), "");

             // We will remove the newly created substring from the actual string and 
             // check if the length of the actual string, cleared of the newly created substring, is 0.
             // If 0 it tells us that it is only made of its substring
             if (clearedOfNewSubstrings.Length == 0)
             {
                 // Next we will return the count of the newly created substring in the actual string.
                 var countOccurences = Regex.Matches(str, substr.ToString()).Count;

                 return countOccurences.ToString();
             }
         }

         return "-1";
     }       

     static void Main(string[] args)
     {
         // Input: {"abcdaabcdaabcda"}
         // Output: 3

         // Input: { "abcdaabcdaabcda" }
         // Output: -1

         // Input: {"barrybarrybarry"}
         // Output: 3            

         var s = "asdf"; // Output will be -1

         Console.WriteLine(CountOfRepeatedSubstring(s));
     }
 }

答案 2 :(得分:-1)

您想如何指定&#34;重复字符串&#34;?它是否只是第一组字符,直到a)再次找到第一个字符,b)模式开始重复,或c)其他一些标准?

所以,如果你的字符串是&#34; ABBAABBA&#34;,那是2,因为&#34; ABBA&#34;重复两次或是1,因为你有&#34; ABB&#34;其次是&#34; AAB&#34;?怎么样&#34; ABCDABCE&#34; - &#34; ABC&#34;计数(尽管&#34; D&#34;在重复之间?)在&#34; ABCDABCABCDABC&#34;中,是重复字符串&#34; ABCD&#34; (1)或&#34; ABCDABC&#34; (2)?

&#34; AAABBAAABB&#34; - 是3(&#34; AAA&#34;)还是2(&#34; AAABB&#34;)?

如果重复字符串的结尾是第一个字母的另一个实例,那很简单:

按字符逐个字符串工作,将每个字符放入另一个变量中,直到下一个字符与第一个字符匹配为止。然后,给定第二个变量中子字符串的长度,检查字符串的下一位以查看它是否匹配。继续,直到它不匹配或你到达字符串的末尾。

如果你只想找到任何长度模式重复,无论第一个字符是否在模式中重复,它就会变得更复杂(但幸运的是,这是计算机擅长的东西)。

你需要逐个字符地在另一个变量中构建一个模式,但是你还需要注意第一个字符重新出现并开始构建第二个子字符串,看它是否与第一个匹配。这可能应该放在一个数组中,因为您可能会遇到第一个字符的第三个(或更多)实例,这会触发需要跟踪另一个可能的匹配。

这并不难,但要记住很多,这是一个相当烦人的问题。你有没有特别的理由这样做?