当前位置 : 主页 > 网页制作 > HTTP/TCP >

重复次数最多的连续重复子串

来源:互联网 收集:自由互联 发布时间:2021-06-16
题目链接:https://cn.vjudge.net/contest/318888#problem/G 题意: 给定一个字符串,求重复次数最多的连续重复子串 思路: 我们先看下罗老师的论文中给出的思路 虽然话很短,但是很难理解。我

题目链接:https://cn.vjudge.net/contest/318888#problem/G

 

题意:

给定一个字符串,求重复次数最多的连续重复子串

 

思路:

我们先看下罗老师的论文中给出的思路

 

虽然话很短,但是很难理解。我就讲一下我刚开始无法理解的一些地方吧,既方便我巩固也方便我今后复习。

 

1、为什么连续出现了K/L+1次?

我们先看这个图片上的例子

r[6] :  a a b a a b a a b a b

r[9] :  a a b a a b a b

先考虑往后匹配可以知道K=9,K/L只是在 r[9]之后重复出现的次数 再加上 r[6]->r[9]之间重复出现的一次

所以是 K/L+1

 

2、为什么S肯定包括了字符r[0],r[L],r[2*L] 中某相邻两个

由于当前S是有两个长度为L的连续重复子串拼接而成的,那意味着S[i]和S[i+L](0≤i<L)必定是一样的字符

而这两个字符位置相差L

而字符r[0],r[L],r[L*2],r[L*3],......中相邻两个的位置差均为L

 

3、为什么只须看字符r[L*i]和r[L*(i+1)]往前和往后各能匹配到多远

其实,当枚举的重复子串长度为i时,我们在枚举r[i*j]和r[i*(j+1)]的过程中,必然可以出现r[i*j]在第一个重复子串里,而r[i*(j+1)]在第二个重复子串里的这种情况,如果此时r[i*j]是第一个重复子串的首字符,这样直接用公共前缀k除以i并向下取整就可以得到最后结果。但如果r[i*j]如果不是首字符,这样算完之后结果就有可能偏小,因为r[i*j]前面可能还有少许字符也能看作是第一个重复子串里的。


于是,我们不妨先算一下,从r[i*j]开始,除匹配了k/i个重复子串,还剩余了几个字符,剩余的自然是k%i个字符。如果说r[i*j]的前面还有i-k%i个字符完成匹配的话,这样就相当于利用多余的字符还可以再匹配出一个重复子串,于是我们只要检查一下从r[i*j-(i-k%i)]和r[i*(j+1)-(i-k%i)]开始是否有i-k%i个字符能够完成匹配即可,也就是说去检查这两个后缀的最长公共前缀是否比i-k%i大即可。
当然如果公共前缀不比i-k%i小,自然就不比i小,因为后面的字符都是已经匹配上的,所以为了方便编写,程序里面就直接去看是否会比i小就可以了。

  1 #include <stdio.h>
  2 #include <iostream>
  3 #include <algorithm>
  4 #include <string.h>
  5 #include <stdlib.h>
  6 #include <math.h>
  7 #include <queue>
  8 #include <set>
  9 
 10 #define INF 0x3f3f3f3f
 11 #define pii pair<int,int>
 12 #define LL long long
 13 using namespace std;
 14 typedef unsigned long long ull;
 15 const int maxn = 5e4 + 10;
 16 
 17 char s[maxn];
 18 int sa[maxn],t[maxn],t2[maxn],c[maxn];
 19 int Rank[maxn],height[maxn];
 20 int dp[maxn][maxn];
 21 
 22 void build_sa(int n,int m)
 23 {
 24     int i,j,*x=t,*y=t2;
 25     for (i=0;i<m;i++)
 26         c[i] = 0;
 27     for (i=0;i<n;i++)
 28         c[x[i] = s[i]]++;
 29     for (i=1;i<m;i++)
 30         c[i] += c[i-1];
 31     for (i=n-1;i>=0;i--)
 32         sa[--c[x[i]]] = i;
 33     for (int k=1;k<=n;k<<=1)
 34     {
 35         int p = 0;
 36         for (i=n-k;i<n;i++)
 37             y[p++] = i;
 38         for (i=0;i<n;i++)
 39         {
 40             if (sa[i]>=k)
 41                 y[p++] = sa[i]-k;
 42         }
 43         for (i=0;i<m;i++)
 44             c[i] = 0;
 45         for (i=0;i<n;i++)
 46             c[x[y[i]]]++;
 47         for (i=1;i<m;i++)
 48             c[i] += c[i-1];
 49         for (i=n-1;i>=0;i--)
 50             sa[--c[x[y[i]]]] = y[i];
 51         swap(x,y);
 52         p = 1;
 53         x[sa[0]] = 0;
 54         for (i=1;i<n;i++)
 55             x[sa[i]] = y[sa[i-1]] == y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
 56         if (p>=n)
 57             break;
 58         m = p;
 59     }
 60 }
 61 
 62 void getHeight(int n){
 63     int i,j,k=0;
 64     for (i=1;i<=n;i++){
 65         Rank[sa[i]] = i;
 66     }
 67     for (i=0;i<n;i++){
 68         if (k)
 69             k--;
 70         j = sa[Rank[i]-1];
 71         while (s[i+k] == s[j+k])
 72             k++;
 73         height[Rank[i]] = k;
 74     }
 75 }
 76 
 77 void ST_build(int n){
 78     for (int i=0;i<n;i++){
 79         dp[i][0] = height[i];
 80     }
 81     for (int j=1;(1<<j)<=n;j++){
 82         for (int i=0;(i+(1<<j)-1)<=n;i++){
 83             dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
 84         }
 85     }
 86 }
 87 
 88 int querry(int i,int j){
 89     int l = min(Rank[i],Rank[j]);
 90     int r = max(Rank[i],Rank[j]);
 91     l++;
 92     int cnt = log2(r-l+1),len = 1<<cnt;
 93     return min(dp[l][cnt],dp[r-len+1][cnt]);
 94 }
 95 
 96 
 97 int main(){
 98     int T;
 99     scanf("%d",&T);
100     while (T--){
101         int maxx = 1;
102         int n;
103         scanf("%d",&n);
104         getchar();
105         for (int i=0;i<n;i++){
106             scanf("%c",&s[i]);
107             getchar();
108         }
109         s[n] = 0;
110         build_sa(n+1,100);
111         getHeight(n);
112         ST_build(n+1);
113         for (int i=1;i<=n;i++){
114             for (int j=0;j+i<n;j+=i){
115                 int ans = querry(j,j+i);
116                 int k = j-(i-ans%i);
117                 ans = ans/i+1;
118                 if (k>=0 && querry(k,k+i)>=i)
119                     ans++;
120                 maxx = max(maxx,ans);
121             }
122         }
123         printf("%d\n",maxx);
124     }
125     return 0;
126 }
网友评论