版权声明:欢迎大佬指正! https://blog.csdn.net/sinat_36215255/article/details/82946750
题目链接https://vjudge.net/problem/POJ-1743
题目大概是给n个数组成的串,求是否有多个“相似”且不重叠的子串的长度大于等于5,两个子串相似当且仅当长度相等且每一位的数字差都相等
本题思路借鉴自:https://www.cnblogs.com/WABoss/p/5199261.html
有个非常巧妙的思路:
- 首先把问题转化成重复子串的问题:把原串每一位都与前一位相减。这样得出的新串如果有两个长度为n的子串相同,那么它们对应在原串的长度n+1的子串也就相似。
最后大概描述一下不可重叠最长重复子串的解法:
- O(logn)二分枚举子串长度,判断解是否成立
- O(n)判断长度是否成立:把互相之间LCP大于等于长度的分为一组,这通过个扫一遍height即可,因为后缀是有序的,相邻的后缀间的LCP必定的极大的;接下来就找到每个组里后缀sa值最大和最小的,如果差值大于(等于)k就成立,因为这样小下标的后缀沿着LCP下去走k步才不会盖到大下标的后缀。
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 22222
#define INF (1<<30)
int wa[MAXN],wb[MAXN],wv[MAXN],ws[MAXN];
int cmp(int *r,int a,int b,int l)
{
return r[a]==r[b] && r[a+l]==r[b+l];
}
int sa[MAXN],t[MAXN],t2[MAXN],c[MAXN],n;
int height[MAXN], rankk[MAXN];
void DA(int s[],int m)
{
int i, *x = t, *y = t2;
for ( i = 0; i < m; i++)
c[i] = 0;
for ( i = 0; i < n; i++)
c[x[i] = s[i]]++;
for ( i = 1; i < m; i++)
c[i] += c[i - 1];
for (i = n - 1; i >= 0; i--)
sa[--c[x[i]]] = i;
for (int k = 1; k <= n; k <<= 1)
{
int p = 0;
for (i = n - k; i < n; i++)
y[p++] = i;
for (i = 0; i < n; i++)
{
if (sa[i] >= k)
y[p++] = sa[i] - k;
}
for (i = 0; i < m; i++)
c[i] = 0;
for (i = 0; i < n; i++)
c[x[y[i]]]++;
for (i = 0; i < m; i++)
c[i] += c[i - 1];
for (i = n - 1; i >= 0; i--)
sa[--c[x[y[i]]]] = y[i];
swap(x, y);
p = 1;
x[sa[0]] = 0;
for (i = 1; i < n; i++)
{
x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++;
}
if (p >= n)
break;
m = p;
}
}
void getHeight(int s[])
{
int i, j, k = 0;
for (i = 0; i < n; i++)
rankk[sa[i]] = i;
for (i = 0; i < n; i++)
{
if (k)
k--;
j = sa[rankk[i] - 1];
while (s[i + k] == s[j + k])
k++;
height[rankk[i]] = k;
}
}
int a[MAXN],r[MAXN];
bool isok(int k)
{
bool flag=0;
int mx=-INF,mm=INF;
for(int i=2; i<=n; ++i)
{
if(height[i]>=k)
{
mm=min(mm,min(sa[i],sa[i-1]));
mx=max(mx,max(sa[i],sa[i-1]));
if(mx-mm>k)
return 1;
}
else
{
mx=-INF,mm=INF;
}
}
return 0;
}
int main()
{
while(~scanf("%d",&n) && n)
{
for(int i=0; i<n; ++i)
scanf("%d",a+i);
--n;
for(int i=0; i<n; ++i)
r[i]=a[i+1]-a[i]+88;
r[n]=0;
DA(r,176);
getHeight(r);
// SA(r,n+1,176);
int l=0,r=n>>1;
int ans = 0;
while(l<=r)
{
int mid=(l+r)>>1;
if(isok(mid))
{
ans = mid;
l=mid+1;
}
else
r=mid-1;
}
if(ans>=4)
printf("%d\n",ans+1);
else
printf("%d\n",0);
}
return 0;
}