后缀数组(不可重叠最长重复子串)

来源:互联网 发布:linux自动登录ssh脚本 编辑:程序博客网 时间:2024/06/07 06:22

poj 1743
二分答案,把题目变成判定性问题:判断是否
存在两个长度为k的子串是相同的,且不重叠。解决这个问题的关键还是利用
height数组。把排序后的后缀分成若干组,其中每组的后缀之间的height值都
不小于k。
有希望成为最长公共前缀不小于k的两个后缀一定在同一组。然
后对于每组后缀,只须判断每个后缀的sa值的最大值和最小值之差是否不小于
k。如果有一组满足,则说明存在,否则不存在。

#include<cstdio>//sa为后缀数组,把后缀从小到大排序把后缀开头存起来,rank为名次数组,以i开头的后缀在所有后缀中排第几#include<cstring>#include<cstdlib>#include<iostream>#include<algorithm>#define F(x) ((x)/3+((x)%3==1?0:tb))#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)using namespace std;const int maxn=1e6+10;int wa[maxn],wb[maxn],ww[maxn],wv[maxn],n;int cmp(int *r,int a,int b,int l){    return r[a]==r[b]&&r[a+l]==r[b+l];}void da(int *r,int *sa,int n,int m){    int i,j,p,*x=wa,*y=wb,*t;    for(i=0; i<m; i++) ww[i]=0;    for(i=0; i<n; i++) ww[x[i]=r[i]]++;    for(i=1; i<m; i++) ww[i]+=ww[i-1];    for(i=n-1; i>=0; i--) sa[--ww[x[i]]]=i; //处理长度为一的字符串,得到sa数组    for(j=1,p=1; p<n; j*=2,m=p) //倍增法求sa    {        for(p=0,i=n-j; i<n; i++) y[p++]=i;        for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;//利用上次的sa直接求出按第二个关键字排序        for(i=0; i<n; i++) wv[i]=x[y[i]]; //第二关键字的排序得出第一关键字的顺序        for(i=0; i<m; i++) ww[i]=0;        for(i=0; i<n; i++) ww[wv[i]]++;        for(i=1; i<m; i++) ww[i]+=ww[i-1];        for(i=n-1; i>=0; i--) sa[--ww[wv[i]]]=y[i]; //根据第一关键字的顺序排出sa数组的顺序        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++) //更新x数组 x为rank数组            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;    }    return ;}int h[maxn];//也就是排名相邻的两个后缀的最长公共前缀sa[i]和sa[i-1]int Rank[maxn];//名次数组void get_height(int *r,int *sa,int n){    int k=0,i,j;    for(int i=1; i<=n; i++) Rank[sa[i]]=i;    for(int i=0; i<n; h[Rank[i++]]=k)        for(k?k--:0,j=sa[Rank[i]-1]; r[i+k]==r[j+k]; k++) ;    return ;}int a[maxn];int sa[maxn],r[maxn];int judge(int mid){    int mn,mx,j;    for(int i=1; i<=n; i=j)    {        while(i<=n&&h[i]<mid)//排除最长前缀小于mid的            i++;        j=i;        mn=n;//记录同一组中sa的最小值        mx=0;//记录同一组中sa的最大值        if(i==n+1)            break;        mn=min(mn,sa[i-1]);        mx=max(mx,sa[i-1]);        while(j<=n&&h[j]>=mid)//大于mid的分在同一组               {            mn=min(mn,sa[j]);            mx=max(mx,sa[j]);            j++;        }        if(mx-mn>=mid)//如果不重复返回1            return 1;    }    return 0;}int main(){    while(~scanf("%d",&n)&&n)    {        for(int i=0; i<n; i++)            scanf("%d",&a[i]);        for(int i=0; i<n-1; i++)        {            a[i]=a[i+1]-a[i]+89;        }        n--;        a[n]=0;        da(a,sa,n+1,180);        get_height(a,sa,n);        int l=0;        int R=n-1;        while(l<=R)//二分枚举长度        {            int mid=(l+R)>>1;            if(judge(mid))                l=mid+1;            else                R=mid-1;        }        int ans=R;        if(ans>=4)            printf("%d\n",ans+1);        else            printf("0\n");    }}
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 黑在美国病了怎么办 在外打工房租太贵怎么办 在外面打工房租租不起怎么办 买车型号错了怎么办 沃出行不退押金怎么办 钢铁雄心4人力0怎么办 钢铁雄心4没工厂怎么办 钢铁雄心4锁区怎么办 qq超市金币满了怎么办 旋转轮胎2车翻了怎么办 轮胎里面卡进小石子应该怎么办 手游吃鸡模拟器已经到达上限怎么办 逆战场手游模拟器黑屏怎么办 欧洲卡车2翻车了怎么办 卧式注塑机锁模久了打不开怎么办 欧洲卡车2困了怎么办 欧卡2没油了怎么办 欧卡2车卡住了怎么办 欧卡2车子卡住了怎么办 做题手感变差怎么办 吃鸡游戏掉帧怎么办 买了俄区游戏怎么办 战地1子弹用完了怎么办 1kb彩信点开了怎么办 玩地球末日卡顿怎么办 电脑显示不亮了怎么办 电脑玩游戏显卡停止运行怎么办 ie浏览器打开就停止工作怎么办 蝙蝠侠阿甘骑士卡顿怎么办 捷豹钥匙没电了怎么办 车钥匙没电 汽车报警怎么办 东西卡在门锁里怎么办 锁芯里面有东西怎么办 有东西卡在锁里怎么办 锁里面卡了牙签怎么办 塑料卡在锁里怎么办 门锁被牙签堵了怎么办 锁子里面卡东西怎么办 东西卡在锁里怎么办 开车门碰到旁边车门怎么办 美团退款后 物品怎么办