【Leetcode】459. Repeated Substring Pattern(配数学证明)

题目地址:

https://leetcode.com/problems/repeated-substring-pattern/

给定一个长 n n n字符串 s s s,问是否是某个字符串自我重复若干次得到的(这里若干次指的是多于 1 1 1次)。

思路是用KMP算法。先求 s s s的next数组 n e ne ne,但这里数组需要多求一位,目的是求出 s s s的最长相等前后缀的长度,设其为 l l l。如果 l = 0 l=0 l=0,那么显然返回false;我们可以证明,当 l > 0 l>0 l>0时,返回true当且仅当 ( n − l ) ∣ n (n-l)|n (nl)n

充分性:
( n − l ) ∣ n (n-l)|n (nl)n,则 n n − l ≥ 2 \frac{n}{n-l}\ge 2 nln2,所以 n ≤ 2 l n\le 2l n2l,设 x = n − l ≤ l x=n-l\le l x=nll,所以 x ∣ n x|n xn,由next数组定义知道, s [ 0 : x − 1 ] = s [ x : 2 x − 1 ] = s [ 2 x : 3 x − 1 ] = . . . = s [ n − x : n − 1 ] s[0:x-1]=s[x:2x-1]=s[2x:3x-1]=...=s[n-x:n-1] s[0:x1]=s[x:2x1]=s[2x:3x1]=...=s[nx:n1],也就是说 s s s s [ 0 : x − 1 ] s[0:x-1] s[0:x1]自我重复若干次得到的。

必要性:
s = A A . . . A s=AA...A s=AA...A,其中 A A A代表某个字符串,也就是 s s s可以表达为某个字符串自我重复 k k k次。当 s s s给定时,我们取最短的那个 A A A,所以此时 k k k也达到最大,有 n = k l A n=kl_A n=klA,则 n e [ n ] ≥ ( k − 1 ) l A ne[n]\ge (k-1)l_A ne[n](k1)lA。下面我们证明 n e [ n ] ≤ ( k − 1 ) l A ne[n]\le (k-1)l_A ne[n](k1)lA。反证法,如果 n e [ n ] ≥ ( k − 1 ) l A + 1 ne[n]\ge (k-1)l_A+1 ne[n](k1)lA+1,设 n e [ n ] = l ne[n]=l ne[n]=l x = n − l x=n-l x=nl,由必要性证明知道, s [ 0 : x − 1 ] = s [ x : 2 x − 1 ] = . . . = s [ u x : ( u + 1 ) x − 1 ] s[0:x-1]=s[x:2x-1]=...=s[ux:(u+1)x-1] s[0:x1]=s[x:2x1]=...=s[ux:(u+1)x1],其中 n − 1 − ( ( u + 1 ) x − 1 ) < x n-1-((u+1)x-1)<x n1((u+1)x1)<x(这个不等式一定是成立的,否则的话 s s s就可以由 s [ 0 : x − 1 ] s[0:x-1] s[0:x1]重复若干次得到,这与 A A A的最短性矛盾),所以有 s [ n − x : ( u + 1 ) x − 1 ] = s [ n − 1 − ( ( u + 1 ) x − 1 ) : x − 1 ] = s [ n − ( u + 1 ) x : x − 1 ] s[n-x:(u+1)x-1]=s[n-1-((u+1)x-1):x-1]\\=s[n-(u+1)x:x-1] s[nx:(u+1)x1]=s[n1((u+1)x1):x1]=s[n(u+1)x:x1]这与 n e [ n ] = l ne[n]=l ne[n]=l是矛盾的,因为 n e [ n ] ne[n] ne[n]还可以拓展,应该有 n e [ n ] > l ne[n]>l ne[n]>l才对。如此一来, n e [ n ] ne[n] ne[n]取任何大于 ( k − 1 ) l A (k-1)l_A (k1)lA的数都会造成矛盾,只能有 n e [ n ] = ( k − 1 ) l A ne[n]= (k-1)l_A ne[n]=(k1)lA。而 n − ( k − 1 ) l A = l A n-(k-1)l_A=l_A n(k1)lA=lA,所以 ( n − ( k − 1 ) l A ) ∣ n (n-(k-1)l_A)|n (n(k1)lA)n。证毕。

所以我们证明了 s s s是由某字符串自我重复若干次得到的,当且仅当 l > 0 ∧ ( n − l ) ∣ n l>0\land (n-l)|n l>0(nl)n。并且顺便知道了,此时 s [ 0 : l − 1 ] s[0:l-1] s[0:l1]就是最短的可以重复若干次得到 s s s的那个字符串。关于next数组怎么求,参考https://blog.csdn.net/qq_46105170/article/details/106168535。代码如下:

class Solution {
 public:
  bool repeatedSubstringPattern(string s) {
    int n = s.size();
    s = ' ' + s;
    int ne[n + 1];
    ne[1] = 0;
    for (int i = 2, j = 0; i <= n; i++) {
      while (j && s[i] != s[j + 1]) j = ne[j];
      if (s[i] == s[j + 1]) j++;
      ne[i] = j;
    }

    return ne[n] && n % (n - ne[n]) == 0;
  }
};

时空复杂度 O ( n ) O(n) O(n)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值