每日一题——重复的子字符串

简介: 每日一题——重复的子字符串

重复的子字符串

题目链接

注:本题的题解基本建立在KMP算法之上,对KMP算法不太了解的小伙伴可以参考这篇文章KMP算法及其改进图文详解

方法一:移动匹配

  • 我们先来看几个可以由一个字串重复多次构成的主字符串:“aaa”,“ababab”,“abcabc”
  • 可以发现满足条件主串的两个规律:
  • 一定可以在其前半部分和后半部分找到相等的子串,如上面三个例子的子串“a”,子串“ab”,子串“abc”
  • 交换前半部分和后半部分都相等的子串,主串仍然不变。
  • 由此可以得到,如果我们将主串复制一份,再将这两个相同的串拼到一起,并去头去尾(防止查找的过程中找到原来的串和复制后的串),如果能在新串中找到和主串相等的子串,那就说明这个主串就可以由它的一个子串重复构成
  • 例如字符串abcabc

实现代码

//获得Next数组
void GetNext(char *s,int *Next)
{
    int len = strlen(s);
    Next[0] = -1;
    int i = 0;
    int k = -1;
    while(i < len - 1)
    {
        if(k == -1 || s[i] == s[k])
        {
            k++;
            i++;
            if(s[i] == s[k])
                Next[i] = Next[k];
            else
                Next[i] = k;
        }
        else
            k = Next[k];
    }
}
//判断是否可以在字符串s1中找到串s2
bool KMP(char *s1, char *s2, int *Next)     //s1为主串,s2为从串
{
    int len1 = strlen(s1);
    int len2 = strlen(s2);
    int i = 0, j = 0;
    while(i < len1 && j < len2)
    {
        if(s1[i] == s2[j])
        {
            i++;
            j++;
        }
        else if(j != 0)
        {
            j = Next[j];
            if(j == -1)
                j = 0;
        }
        else
            i++;
    }
    if(j >= len2)
        return true;
    else
        return false;
}
bool repeatedSubstringPattern(char * s){
    int i,j;
    int len = strlen(s);
    char *str = (char *)malloc(sizeof(char) * 2 * len + 1);
    int *Next = (int *)malloc(sizeof(int) * len);
    GetNext(s,Next);  //获得Next数组
    //将串s和其复制串合并到str中
    for(i = 0; i < len; i++)
        str[i] = s[i];
    j = i;
    for(i = 0; i < len; i++)
        str[j + i] = s[i];
    str[2 * len] = '\0';
    //去尾
    str[2 * len - 1] = '\0';
    return KMP(str + 1,s,Next);   //str+1相当于去头
}

方法二:KMP

  • 我们知道,KMP算法中,有Next数组保存了每个字符之前的子串的最长相等前后缀长度,那么最长相等前后缀长度和最小的重复子串有什么关系呢?
  • 直接下结论:
  • 如果一个字符串是由重复的子串构成,那么最长相等前后缀不包含的子串就是最小的重复子串
  • 如果一个字符串没有相等前后缀,那么这个字符串一定不能由重复子串构成
  • 由于本篇Next数组记录的是每个字符之前的子串的最长相等前后缀长度,因此为了判断该字符串是否具有相等的前后缀,需要在字符串的末尾再添加一个字符(随便什么字符都可以),这样,如果字符串没有相等的前后缀,那么Next[len - 1] = 0(len为Next数组大小)
  • 字符串的最大相等前后缀长度为Next[len - 1],由第一个结论可得,最小重复子串为len - next[len - 1],若len % (len - next[len - 1]) == 0 ,就说明字符串长度可以整除最小重复子串的长度,即可说明该字符串可以由重复子串构成。

实现代码

//求Next数组
void GetNext(char *s,int *Next)
{
    int len = strlen(s);
    Next[0] = -1;
    int i = 0;
    int k = -1;
    while(i < len - 1)
    {
        if(k == -1 || s[i] == s[k])
        {
            k++;
            i++;
            Next[i] = k;
        }
        else
            k = Next[k];
    }
}
bool repeatSubstring(char* str ) {
    int len = strlen(str);
    //创建一个比原字符串大两个字符(一个随机字符,和空字符)的字符数组
    char* s = (char*)malloc(sizeof(char) * (len + 2));    
    //将原字符串拷贝到新字符串中,并添加随机字符和空字符
    strcpy(s,str);
    s[len] = 'a';
    s[len + 1] = '\0';
    //申请Next数组的内存
    int *Next = (int *)malloc(sizeof(int) * (len + 1));
    //得到Next数组
    GetNext(s,Next);
    //如果该字符串由相等前后缀,并且字符串长度可以整除最小重复子串,那么就说明这个字符串可以由重复子串构成
    //这里的len之所以不要减一,是因为Next的大小为len+1,我们要得到的就是Next的最后一个值,即Next[len]
    if(Next[len] != 0 && len % (len - Next[len]) == 0)
        return true;
    return false;
}


相关文章
|
8月前
|
存储 算法 Java
LeetCode 无重复字符的最长子串 打败100%的人
LeetCode 无重复字符的最长子串 打败100%的人
99 0
|
3月前
【LeetCode 22】459.重复的子字符串
【LeetCode 22】459.重复的子字符串
33 0
|
8月前
|
算法
每日一题:LeetCode-LCR 016. 无重复字符的最长子串
每日一题:LeetCode-LCR 016. 无重复字符的最长子串
|
8月前
|
索引
leetcode-1668:最大重复子字符串
leetcode-1668:最大重复子字符串
63 0
|
8月前
|
算法 C++ Python
leetcode-459:重复的子字符串
leetcode-459:重复的子字符串
43 0
|
8月前
|
算法
六六力扣刷题字符串之重复的子字符串
六六力扣刷题字符串之重复的子字符串
71 0
|
算法 索引
代码随想录算法训练营第九天 | LeetCode 8. 找出字符串中第一个匹配项的下标、LeetCode 459. 重复的子字符串
代码随想录算法训练营第九天 | LeetCode 8. 找出字符串中第一个匹配项的下标、LeetCode 459. 重复的子字符串
41 0
|
算法 C语言 C++
Leetcode 每日一题 1234. 替换子串得到平衡字符串
有一个只含有 'Q', 'W', 'E', 'R' 四种字符,且长度为 n 的字符串
50 0
Leetcode 每日一题 1234. 替换子串得到平衡字符串
LeetCode 1668.最大重复子字符串
LeetCode 1668.最大重复子字符串
83 0
|
算法
leetcode 459 重复的子字符串
leetcode 459 重复的子字符串
68 0
leetcode 459 重复的子字符串