CF 427D 后缀数组

来源:互联网 发布:在windows上安装spark 编辑:程序博客网 时间:2024/05/16 12:24

题意:求两串字符串的公共最短子串,且这个子串只在任意一串中出现一次、

首先明确后缀数组将所有的后缀(也可以视为第i个点开始的子串)字典序排列,将最近似的子串集合在一起。

LCP求的是附近两个子串的最长公共前缀

如果某一个子串当且仅当出现两次,则必须是LCP[i]是局部最大。该子串最大长度为LCP[i],最短长度为max(lcp[i+1],lcp[i-1])+1;

同理,可以推出仅出现N次的子串(后缀自动机啦。)


#include <map>#include <set>#include <queue>#include <stack>#include <math.h>#include <vector>#include <cstdio>#include <string>#include<string.h>#include <fstream>#include <iostream>#include <algorithm>using namespace std;#define exp 1e-8#define INF 100000000#define ll long long#define set(a,b) memset(a,b,sizeof(a));#define for1(a,b,c) for(int a=1;a<=b;a+=c)//1---(b)#define for0(a,b,c) for(int a=0;a<b;a+=c)//0---(b-1)void bug(string st="bug"){cout<<st<<endl;}template<typename __ll>inline void READ(__ll &m){    __ll x=0,f=1;char ch=getchar();    while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    m=x*f;}template<typename __ll>inline void read(__ll &m){READ(m);}template<typename __ll>inline void read(__ll &m,__ll &a){READ(m);READ(a);}template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b){READ(m);READ(a);READ(b);}const int maxn=50000; //字符串总长度int scnt,k;//,k为被增发的kint rank[maxn];//第几位开始的后缀数组排第几位int sa[maxn];//对scnt个后缀数组排序后,第几位对应字符串的第几位//第一位为空字符串,即最后一位的‘\0’,在这个模板里,最后一位以‘\0’链接int lcp[maxn];//最长公共前缀,最终排好序的后缀子串i与子串i+1的最长公共前缀的长度int tmp[maxn];//协助更新bool cmp_sa(int i,int j){    if(rank[i]!=rank[j]) return rank[i]<rank[j];    int ri=i+k<=scnt?rank[i+k]:-1;    int rj=j+k<=scnt?rank[j+k]:-1;    return ri<rj;}void calc_sa(char *S)//计算字符串的后缀数组{    scnt=strlen(S);    for(int i=0;i<=scnt;i++) //初始化    {        sa[i]=i;        rank[i]=i<scnt?S[i]:-1;  //最后一位补上-1!    }    for(k=1;k<=scnt;k*=2)//!!注意此处是k=1,而不是int k=1;!!!    {        sort(sa,sa+scnt+1,cmp_sa);        tmp[sa[0]]=0;        for(int i=1;i<=scnt;i++)  //int i=1;            tmp[sa[i]]=tmp[sa[i-1]]+(cmp_sa(sa[i-1],sa[i])?1:0);        for(int i=0;i<=scnt;i++) //int i=0;            rank[i]=tmp[i];    }}void construct_lcp(char *S){    scnt=strlen(S);    for(int i=0;i<=scnt;i++)        rank[sa[i]]=i;    int h=0;    lcp[0]=0;    for(int i=0;i<scnt;i++)    {        int j=sa[rank[i]-1];        if(h>0) h--;        for(;j+h<scnt&&i+h<scnt;h++)            if(S[j+h]!=S[i+h]) break;        lcp[rank[i]-1]=h;    }}//bool contain(char S[],char T[])//S为母串。检查T是否为子串void Union(char S[],char T[]){    int a=strlen(S);    S[a]='$';  //此处以‘$’链接两个串    S[a+1]='\0';    strcat(S,T);}int main(){        char S[maxn],T[maxn];        scanf("%s",&S);        int l=strlen(S);        scanf("%s",&T);        Union(S,T);        int len=strlen(S);        calc_sa(S);        construct_lcp(S);        int ans=100000000;        for(int i=1;i<len;i++)            if(lcp[i]>lcp[i-1]&&lcp[i]>lcp[i+1]&&((sa[i]<l)!=(sa[i+1]<l)))   //求出符合的子串                ans=min(ans,max(lcp[i-1],lcp[i+1])+1);  //更新最短的长度        if(ans==100000000)            printf("-1\n");        else printf("%d\n",ans);    return 0;}



0 0
原创粉丝点击