zoukankan      html  css  js  c++  java
  • 「kuangbin带你飞」专题十八 后缀数组


    layout: post
    title: 「kuangbin带你飞」专题十八 后缀数组
    author: "luowentaoaa"
    catalog: true
    tags:
    - kuangbin
    - 字符串
    - 后缀数组


    传送门

    倍增法

    struct DA{
        bool cmp(int *r,int a,int b,int l){
            return r[a]==r[b]&&r[a+l]==r[b+l];
        }
        int t1[maxn],t2[maxn],c[maxn];
        int rank[maxn],height[maxn],RMQ[maxn],mm[maxn];
        int best[20][maxn];
        int r[maxn];
        int sa[maxn];
        int str[maxn];
        void da(int n,int m){
            n++;
            int i,j,p,*x=t1,*y=t2;
            for(i=0;i<m;i++)c[i]=0;
            for(i=0;i<n;i++)c[x[i]=str[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(j=1;j<=n;j<<=1){
                p=0;
                for(i=n-j;i<n;i++)y[p++]=i;
                for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
                for(i=0;i<m;i++)c[i]=0;
                for(i=0;i<n;i++)c[x[y[i]]]++;
                for(i=1;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]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
                if(p>=n)break;
                m=p;
            }
            int k=0;
            n--;
            for(i=0;i<=n;i++)rank[sa[i]]=i;
            for(i=0;i<n;i++){
                if(k)k--;
                j=sa[rank[i]-1];
                while(str[i+k]==str[j+k])k++;
                height[rank[i]]=k;
            }
        }
        void initRMQ(int n){
            for(int i=1;i<=n;i++)RMQ[i]=height[i];
            mm[0]=-1;
            for(int i=1;i<=n;i++)
                mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
            for(int i=1;i<=n;i++)best[0][i]=i;
            for(int i=1;i<=mm[n];i++)
            for(int j=1;j+(1<<i)-1<=n;j++){
                int a=best[i-1][j];
                int b=best[i-1][j+(1<<(i-1))];
                if(RMQ[a]<RMQ[b])best[i][j]=a;
                else best[i][j]=b;
            }
        }
        int askRMQ(int a,int b){
            int t;
            t=mm[b-a+1];
            b-=(1<<t)-1;
            a=best[t][a];b=best[t][b];
            return RMQ[a]<RMQ[b]?a:b;
        }
        int lcp(int a,int b){
            a=rank[a];b=rank[b];
            if(a>b)swap(a,b);
            return height[askRMQ(a+1,b)];
        }
        void print(int n){
            cout<<"sa[] ";
            for(int i=0;i<=n;i++)cout<<sa[i]<<" ";cout<<endl;
            cout<<"rank[] ";
            for(int i=0;i<=n;i++)cout<<rank[i]<<" ";cout<<endl;
            cout<<"height[] ";
            for(int i=0;i<=n;i++)cout<<height[i]<<" ";cout<<endl;
        }
    }AA,BB;
    

    A.POJ1743 Musical Theme

    题意

    有N(1<=N<=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的子串,它需要满足如下条件:1.长度至少为5个音符。
    2.在乐曲中重复出现(就是出现过至少两次)。(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值)
    3.重复出现的同一主题不能有公共部分。

    思路

    对于第一二点:至少长度是五,我们可以二分0-n/2之间的长度来查找height(height[i]=排名为i和i-1的后缀子串的最长公共前缀长度缀)然后找到两个大于二分的值,然后判断起点距离是否大于二分的值(因为要求不重叠)

    第三点 因为共同加上一个值,但是他们前后的差值还是一样的(例如1,2,3和5,6,7是一样的他们的差值是1)然后把原数组变成一个长度为N-1的数组去处理,因为处理后的数组比原数组少1所以答案也要-1;

    //#include<bits/stdc++.h>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    typedef long long ll;
    const ll mod=998244353;
    const int maxn=2e5;
    const ll inf=0x3f3f3f3f3f3f;
    #define bug cout<<"here:"<<endl;
    int sa[maxn];
    //sa[i]名次为i的后缀的起始位置;
    //Rank[i]起始位置为i的名次 ->Rank[sa[i]]=i;
    //height[i]名次为i和i-1的后缀的最长公共前缀长;
    int t1[maxn],t2[maxn],c[maxn];
    int Rank[maxn],height[maxn];
    bool cmp(int *r,int a,int b,int l){
        return r[a]==r[b]&&r[a+l]==r[b+l];
    }
    int s[maxn];
    void da(int str[],int n,int m){
        int i,j,p,*x=t1,*y=t2;
        for(i=0;i<m;i++)c[i]=0;
        for(i=0;i<n;i++)c[x[i]=str[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(j=1;j<=n;j<<=1){
            p=0;
            for(i=n-j;i<n;i++)y[p++]=i;
            for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
            for(i=0;i<m;i++)c[i]=0;
            for(i=0;i<n;i++)c[x[y[i]]]++;
            for(i=1;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]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
            if(p>=n)break;
            m=p;
        }
        int k=0;
        n--;
        for(i=0;i<=n;i++)Rank[sa[i]]=i;
        for(i=0;i<n;i++){
            if(k)k--;
            j=sa[Rank[i]-1];
            while(s[i+k]==s[j+k])k++;
            height[Rank[i]]=k;
        }
    }
    
    bool isok(int n,int k){
        int ma=sa[1],mi=sa[1];
        for(int i=2;i<=n;i++){
            if(height[i]<k)ma=mi=sa[i];
            else{
                if(sa[i]<mi)mi=sa[i];
                if(sa[i]>ma)ma=sa[i];
                if(ma-mi>k)return true;
            }
        }return false;
    }
    int main()
    {
        std::ios::sync_with_stdio(false);
        std::cin.tie(0);
        std::cout.tie(0);
        int n;
        while(cin>>n&&n){
            for(int i=0;i<n;i++)cin>>s[i];
            for(int i=n-1;i>0;i--)s[i]=s[i]-s[i-1]+90;//因为四十行要用s[i]的值做下表所以不能出现负数
            n--;//变化后的长度-1
            for(int i=0;i<n;i++)s[i]=s[i+1];
            s[n]=0;
            da(s,n+1,200);
    
            int ans=-1;
            int l=1,r=n/2;
            while(l<=r){
                int mid=(l+r)/2;
                if(isok(n,mid)){
                    ans=mid;
                    l=mid+1;
                }
                else r=mid-1;
            }
            if(ans<4)cout<<0<<endl;
            else cout<<ans+1<<endl;
        }
        return 0;
    }
    
    

    附加1. #1403 : 后缀数组一·重复旋律

    题意

    最长可重叠重复K次子串问题。

    题解

    二分答案

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define pp pair<int,int>
    #define rank rankk
    const ll mod=998244353;
    const int maxn=1e6+50;
    const ll inf=0x3f3f3f3f3f3f3f3fLL;
    int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
    int lcm(int a,int b){return a*b/gcd(a,b);}
    int t1[maxn],t2[maxn],c[maxn];
    bool cmp(int *r,int a,int b,int l){
        return r[a]==r[b]&&r[a+l]==r[b+l];
    }
    void da(int str[],int sa[],int rank[],int height[],int n,int m){
        n++;
        int i,j,p,*x=t1,*y=t2;
        for(i=0;i<m;i++)c[i]=0;
        for(i=0;i<n;i++)c[x[i]=str[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(j=1;j<=n;j<<=1){
            p=0;
            for(i=n-j;i<n;i++)y[p++]=i;
            for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
            for(i=0;i<m;i++)c[i]=0;
            for(i=0;i<n;i++)c[x[y[i]]]++;
            for(i=1;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]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
            if(p>=n)break;
            m=p;
        }
        int k=0;
        n--;
        for(i=0;i<=n;i++)rank[sa[i]]=i;
        for(i=0;i<n;i++){
            if(k)k--;
            j=sa[rank[i]-1];
            while(str[i+k]==str[j+k])k++;
            height[rank[i]]=k;
        }
    }
    int rank[maxn];//后缀i在sa[]中的排名
    int height[maxn];//sa[i]与sa[i-1]的LCP(最长公共前缀)
    int str[maxn];
    int r[maxn];
    int sa[maxn];//sa[i]表示排名弟i小的后缀的下标
    int n,m;
    int solve(int k){
        int tmp=1;
        for(int i=1;i<n;){
            int cnt=1;
            int j=i+1;
            while(height[j]>=k&&j<=n)j++,cnt++;
            tmp=max(cnt,tmp);
            i=j;
        }
        return tmp>=m;
    }
    int main()
    {
        std::ios::sync_with_stdio(false);
        std::cin.tie(0);
        std::cout.tie(0);
        cin>>n>>m;
        for(int i=0;i<n;i++)cin>>str[i];
        str[n]=0;//多补一个0
        da(str,sa,rank,height,n,128);
        int l=0,r=n,ans=-1;
        while(l<=r){
            int mid=(l+r)>>1;
            if(solve(mid))ans=mid,l=mid+1;
            else r=mid-1;
        }
        cout<<ans<<endl;
        return 0;
    }
    

    附加2. #1407 : 后缀数组二·重复旋律2

    题意

    最长不可重叠重复子串问题

    题解

    只要判断长度是否大于二分的就行

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define pp pair<int,int>
    #define rank rankk
    const ll mod=998244353;
    const int maxn=1e6+50;
    const ll inf=0x3f3f3f3f3f3f3f3fLL;
    int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
    int lcm(int a,int b){return a*b/gcd(a,b);}
    int t1[maxn],t2[maxn],c[maxn];
    bool cmp(int *r,int a,int b,int l){
        return r[a]==r[b]&&r[a+l]==r[b+l];
    }
    void da(int str[],int sa[],int rank[],int height[],int n,int m){
        n++;
        int i,j,p,*x=t1,*y=t2;
        for(i=0;i<m;i++)c[i]=0;
        for(i=0;i<n;i++)c[x[i]=str[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(j=1;j<=n;j<<=1){
            p=0;
            for(i=n-j;i<n;i++)y[p++]=i;
            for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
            for(i=0;i<m;i++)c[i]=0;
            for(i=0;i<n;i++)c[x[y[i]]]++;
            for(i=1;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]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
            if(p>=n)break;
            m=p;
        }
        int k=0;
        n--;
        for(i=0;i<=n;i++)rank[sa[i]]=i;
        for(i=0;i<n;i++){
            if(k)k--;
            j=sa[rank[i]-1];
            while(str[i+k]==str[j+k])k++;
            height[rank[i]]=k;
        }
    }
    int rank[maxn];//后缀i在sa[]中的排名
    int height[maxn];//sa[i]与sa[i-1]的LCP(最长公共前缀)
    int str[maxn];
    int r[maxn];
    int sa[maxn];//sa[i]表示排名弟i小的后缀的下标
    int n,m;
    int solve(int k){
        int minsa,maxsa;
        for(int i=1;i<=n;i++)
        if(height[i]<k){
            minsa=sa[i];
            maxsa=sa[i];
        }
        else{
            minsa=min(minsa,sa[i]);
            maxsa=max(maxsa,sa[i]);
            if(maxsa-minsa>=k)return true;
        }
        return false;
    }
    int main()
    {
        std::ios::sync_with_stdio(false);
        std::cin.tie(0);
        std::cout.tie(0);
        cin>>n;
        for(int i=0;i<n;i++)cin>>str[i];
        str[n]=0;//多补一个0
        da(str,sa,rank,height,n,1280);
        int l=0,r=n,ans=-1;
        while(l<=r){
            int mid=(l+r)>>1;
            if(solve(mid))ans=mid,l=mid+1;
            else r=mid-1;
        }
        cout<<ans<<endl;
        return 0;
    }
    

    附加3.#1415 : 后缀数组三·重复旋律3

    题意

    经典的最长公共子串问题//

    如果同一段旋律在作品A和作品B中同时出现过,这段旋律就是A和B共同的部分,比如在abab 在 bababab 和 cabacababc 中都出现过。小Hi想知道两部作品的共同旋律最长是多少?

    题解

    把两个字符串接在一起就变成了求后缀的最长公共前缀的问题,但是由于这个前缀不能跨越两个字符串,所以我们在第一个字符串后面加上一个没有出现过的字符,再接第二个字符串,然后取所有非同一个串后缀的height的最大值...

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define pp pair<int,int>
    #define rank rankk
    const ll mod=998244353;
    const int maxn=1e6+50;
    const ll inf=0x3f3f3f3f3f3f3f3fLL;
    int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
    int lcm(int a,int b){return a*b/gcd(a,b);}
    int t1[maxn],t2[maxn],c[maxn];
    bool cmp(int *r,int a,int b,int l){
        return r[a]==r[b]&&r[a+l]==r[b+l];
    }
    void da(char str[],int sa[],int rank[],int height[],int n,int m){
        n++;
        int i,j,p,*x=t1,*y=t2;
        for(i=0;i<m;i++)c[i]=0;
        for(i=0;i<n;i++)c[x[i]=str[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(j=1;j<=n;j<<=1){
            p=0;
            for(i=n-j;i<n;i++)y[p++]=i;
            for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
            for(i=0;i<m;i++)c[i]=0;
            for(i=0;i<n;i++)c[x[y[i]]]++;
            for(i=1;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]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
            if(p>=n)break;
            m=p;
        }
        int k=0;
        n--;
        for(i=0;i<=n;i++)rank[sa[i]]=i;
        for(i=0;i<n;i++){
            if(k)k--;
            j=sa[rank[i]-1];
            while(str[i+k]==str[j+k])k++;
            height[rank[i]]=k;
        }
    }
    int rank[maxn];//后缀i在sa[]中的排名
    int height[maxn];//sa[i]与sa[i-1]的LCP(最长公共前缀)
    char str[maxn];
    char str2[maxn];
    int r[maxn];
    int sa[maxn];//sa[i]表示排名弟i小的后缀的下标
    int n,m;
    int solve(int n,int len1){
        int ans=0;
        for(int i=1;i<=n;i++){
            if((sa[i-1]<=len1&&sa[i]>len1)||(sa[i-1]>len1&&sa[i]<=len1))
                ans=max(ans,height[i]);
        }
        return ans;
    }
    int main()
    {
        std::ios::sync_with_stdio(false);
        std::cin.tie(0);
        std::cout.tie(0);
        cin>>str;
        int len1=strlen(str);
        str[len1]='#';
        cin>>str2;
        int len2=strlen(str2);
        for(int i=0;i<len2;i++){
            str[len1+1+i]=str2[i];
        }
        str[len1+1+len2]=0;
        n=len1+len2+1;
        da(str,sa,rank,height,n,128);
        cout<<solve(n,len1)<<endl;
        return 0;
    }
    

    附加4.#1419 : 后缀数组四·重复旋律4

    题意

    重复次数最多的连续字串

    小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一个音乐旋律被表示为长度为 N 的数构成的数列。小Hi在练习过很多曲子以后发现很多作品中的旋律有重复的部分。

    我们把一段旋律称为(k,l)-重复的,如果它满足由一个长度为l的字符串重复了k次组成。 如旋律abaabaabaaba是(4,3)重复的,因为它由aba重复4次组成。

    小Hi想知道一部作品中k最大的(k,l)-重复旋律。

    题解

    求重复次数最多的连续字串

    假如知道了这个连续子串的开始位置
    并且知道了它的长度
    那么,此时我们就通过lcp(i,i+len)来进行比较即可

    但是,如果枚举长度和开始的位置的话
    尽管lcp通过st表可以O(1)查询
    但是这个枚举的复杂度是O(n2)的
    很显然,需要更快

    所以,枚举了长度lenlen之后
    我们只需要考虑开始位置为lenlen倍数的地方
    如果此时有一个重复串的开始位置不在lenlen的倍数上
    很显然的
    lcp就会多出一截
    所以,我们可以倒推出这个位置
    所以,这样枚举复杂度为O(nlogn)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define pp pair<int,int>
    #define rank rankk
    const ll mod=998244353;
    const int maxn=1e6+50;
    const ll inf=0x3f3f3f3f3f3f3f3fLL;
    int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
    int lcm(int a,int b){return a*b/gcd(a,b);}
    int t1[maxn],t2[maxn],c[maxn];
    bool cmp(int *r,int a,int b,int l){
        return r[a]==r[b]&&r[a+l]==r[b+l];
    }
    void da(char str[],int sa[],int rank[],int height[],int n,int m){
        n++;
        int i,j,p,*x=t1,*y=t2;
        for(i=0;i<m;i++)c[i]=0;
        for(i=0;i<n;i++)c[x[i]=str[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(j=1;j<=n;j<<=1){
            p=0;
            for(i=n-j;i<n;i++)y[p++]=i;
            for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
            for(i=0;i<m;i++)c[i]=0;
            for(i=0;i<n;i++)c[x[y[i]]]++;
            for(i=1;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]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
            if(p>=n)break;
            m=p;
        }
        int k=0;
        n--;
        for(i=0;i<=n;i++)rank[sa[i]]=i;
        for(i=0;i<n;i++){
            if(k)k--;
            j=sa[rank[i]-1];
            while(str[i+k]==str[j+k])k++;
            height[rank[i]]=k;
        }
    }
    int rank[maxn];//后缀i在sa[]中的排名
    int height[maxn];//sa[i]与sa[i-1]的LCP(最长公共前缀)
    int RMQ[maxn];
    int mm[maxn];
    int best[20][maxn];
    void initRMQ(int n){
        mm[0]=-1;
        for(int i=1;i<=n;i++)
            mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
        for(int i=1;i<=n;i++)best[0][i]=i;
        for(int i=1;i<=mm[n];i++)
        for(int j=1;j+(1<<i)-1<=n;j++){
            int a=best[i-1][j];
            int b=best[i-1][j+(1<<(i-1))];
            if(RMQ[a]<RMQ[b])best[i][j]=a;
            else best[i][j]=b;
        }
    }
    int askRMQ(int a,int b){
        int t;
        t=mm[b-a+1];
        b-=(1<<t)-1;
        a=best[t][a];b=best[t][b];
        return RMQ[a]<RMQ[b]?a:b;
    }
    int lcp(int a,int b){//求以a,b开始的字串的最长公共前缀
        a=rank[a];b=rank[b];
        if(a>b)swap(a,b);
        return height[askRMQ(a+1,b)];
    }
    char str[maxn];
    int r[maxn];
    int sa[maxn];//sa[i]表示排名弟i小的后缀的下标
    int n,m;
    int main()
    {
        std::ios::sync_with_stdio(false);
        std::cin.tie(0);
        std::cout.tie(0);
        cin>>str;
        n=strlen(str);
        str[n]=0;
        da(str,sa,rank,height,n,128);
        for(int i=1;i<=n;i++)RMQ[i]=height[i];
        initRMQ(n);
        int ans=1;
        for(int i=1;i<=n;i++){
            for(int j=0;j+i<n;j+=i){
                int len=lcp(j,j+i);
                int k=j-(i-len%i);
                int sum=len/i+1;
                if(k>=0&&lcp(k,k+i)>=i){
                    sum++;
                }
                ans=max(ans,sum);
            }
        }
        cout<<ans<<endl;
        return 0;
    }
    

    B.UVA - 10829 Gap Substrings

    题意

    题目即求有多少子串满足ABA的形式,并且满足|A|>0,|B|=G。

    题解

    搞了我三天三夜的题目

    首先可以用后缀数组求出任意两个点的后缀的lcp,然后相同长度减去g就是答案

    但是这样复杂度是n^2

    然后我们可以发现假设A的长度是L,第一个L在K位置,那么另一个L是K+L+G

    然后我们发现如果L和另一个K+L+G判断相同了之后,那么L+1,L+2,...2L-1其实都不用再匹配了

    所以我们就直接枚举长度L 然后在0,L,2L,3L进行匹配,注意不仅仅要向后面匹配前缀,还要向前面匹配后缀,后者我们可以把字符串倒置,然后求n-1-k,和n-1-(k+l+g)的lcp 这样两个lcp相加,那A的起点就可以在他们这个范围里面 所以答案就是lcp1+lcp2-L

    注意lcp长度不能超过L,因为超过的话就会访问到另一个L的区域了

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define pp pair<int,int>
    const ll mod=998244353;
    const int maxn=1e6+50;
    const ll inf=0x3f3f3f3f3f3f3f3fLL;
    int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
    int lcm(int a,int b){return a*b/gcd(a,b);}
    struct DA{
        bool cmp(int *r,int a,int b,int l){
            return r[a]==r[b]&&r[a+l]==r[b+l];
        }
        int t1[maxn],t2[maxn],c[maxn];
        int rank[maxn],height[maxn],RMQ[maxn],mm[maxn];
        int best[20][maxn];
        int r[maxn];
        int sa[maxn];
        int str[maxn];
        void da(int n,int m){
            n++;
            int i,j,p,*x=t1,*y=t2;
            for(i=0;i<m;i++)c[i]=0;
            for(i=0;i<n;i++)c[x[i]=str[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(j=1;j<=n;j<<=1){
                p=0;
                for(i=n-j;i<n;i++)y[p++]=i;
                for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
                for(i=0;i<m;i++)c[i]=0;
                for(i=0;i<n;i++)c[x[y[i]]]++;
                for(i=1;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]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
                if(p>=n)break;
                m=p;
            }
            int k=0;
            n--;
            for(i=0;i<=n;i++)rank[sa[i]]=i;
            for(i=0;i<n;i++){
                if(k)k--;
                j=sa[rank[i]-1];
                while(str[i+k]==str[j+k])k++;
                height[rank[i]]=k;
            }
        }
        void initRMQ(int n){
            for(int i=1;i<=n;i++)RMQ[i]=height[i];
            mm[0]=-1;
            for(int i=1;i<=n;i++)
                mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
            for(int i=1;i<=n;i++)best[0][i]=i;
            for(int i=1;i<=mm[n];i++)
            for(int j=1;j+(1<<i)-1<=n;j++){
                int a=best[i-1][j];
                int b=best[i-1][j+(1<<(i-1))];
                if(RMQ[a]<RMQ[b])best[i][j]=a;
                else best[i][j]=b;
            }
        }
        int askRMQ(int a,int b){
            int t;
            t=mm[b-a+1];
            b-=(1<<t)-1;
            a=best[t][a];b=best[t][b];
            return RMQ[a]<RMQ[b]?a:b;
        }
        int lcp(int a,int b){
            a=rank[a];b=rank[b];
            if(a>b)swap(a,b);
            return height[askRMQ(a+1,b)];
        }
        void print(int n){
            cout<<"sa[] ";
            for(int i=0;i<=n;i++)cout<<sa[i]<<" ";cout<<endl;
            cout<<"rank[] ";
            for(int i=0;i<=n;i++)cout<<rank[i]<<" ";cout<<endl;
            cout<<"height[] ";
            for(int i=0;i<=n;i++)cout<<height[i]<<" ";cout<<endl;
        }
    }AA,BB;
    char s[maxn];
    int main()
    {
        std::ios::sync_with_stdio(false);
        std::cin.tie(0);
        std::cout.tie(0);
        /*int len=strlen(s);
        for(int i=0;i<len;i++)AA.str[i]=s[i];
        AA.da(len,120);
        AA.print(len);*/
        int t;
        cin>>t;
        int CC=1;
        while(t--){
            int g;
            cin>>g>>s;
            int len=strlen(s);
            //cout<<"s=="<<s<<endl;
            //cout<<"len="<<len<<endl;
            for(int i=0;i<len;i++){
                AA.str[i]=s[i]-'a'+1;
                BB.str[i]=s[len-i-1]-'a'+1;
            }
            AA.da(len,30);BB.da(len,30);
            AA.initRMQ(len);BB.initRMQ(len);
            //AA.print(len);
           // BB.print(len);
            ll ans=0;
            for(int i=1;i<=len;i++){
                for(int j=0;j+i+g<len;j+=i){
                    int l=j,r=j+i+g;
                    int lll=min(i,AA.lcp(l,r));
                    int rrr=min(i,BB.lcp(len-l-1,len-r-1));
                    int len=(lll+rrr);
                    if(len>=i)ans+=ll(1LL*len-i);
                }
            }
            cout<<"Case "<<CC++<<": ";
            cout<<ans<<endl;
        }
        return 0;
    }
    /*
    2
    1 bbaabaaaaa
    5 abxxxxxab
    */
    

    dc3

    struct DA{
        #define F(x) ((x)/3+((x)%3==1?0:tb))
        #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
        int sa[maxn*20],rank[maxn*20],height[maxn*20],str[maxn*20];
        int wa[maxn*20],wb[maxn*20],wv[maxn*20],wss[maxn*20];
        int c0(int *r,int a,int b){
            return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];
        }
        int c12(int k,int *r,int a,int b){
            if(k==2)
                return r[a]<r[b]||(r[a]==r[b]&&c12(1,r,a+1,b+1));
            else return r[a]<r[b]||(r[a]==r[b]&&wv[a+1]<wv[b+1]);
        }
        void sort(int *r,int *a,int *b,int n,int m){
            int i;
            for(i=0;i<n;i++)wv[i]=r[a[i]];
            for(i=0;i<m;i++)wss[i]=0;
            for(i=0;i<n;i++)wss[wv[i]]++;
            for(i=1;i<m;i++)wss[i]+=wss[i-1];
            for(i=n-1;i>=0;i--)
                b[--wss[wv[i]]]=a[i];
        }
        void dc3(int *r,int *sa,int n,int m){
            int i,j,*rn=r+n;
            int *san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
            r[n]=r[n+1]=0;
            for(i=0;i<n;i++)if(i%3!=0)wa[tbc++]=i;
            sort(r+2,wa,wb,tbc,m);
            sort(r+1,wb,wa,tbc,m);
            sort(r,wa,wb,tbc,m);
            for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
                rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
            if(p<tbc)dc3(rn,san,tbc,p);
            else for(i=0;i<tbc;i++)san[rn[i]]=i;
            for(i=0;i<tbc;i++)if(san[i]<tb)wb[ta++]=san[i]*3;
            if(n%3==1)wb[ta++]=n-1;
            sort(r,wb,wa,ta,m);
            for(i=0;i<tbc;i++)wv[wb[i]=G(san[i])]=i;
            for(i=0,j=0,p=0;i<ta&&j<tbc;p++)
                sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
            for(;i<ta;p++)sa[p]=wa[i++];
            for(;j<tbc;p++)sa[p]=wb[j++];
        }
        void da(int n,int m){
            for(int i=n;i<n*3;i++)str[i]=0;
            dc3(str,sa,n+1,m);
            int i,j,k=0;
            for(i=0;i<=n;i++)rank[sa[i]]=i;
            for(i=0;i<n;i++){
                if(k)k--;
                j=sa[rank[i]-1];
                while(str[i+k]==str[j+k])k++;
                height[rank[i]]=k;
            }
        }
        void print(int n){
            cout<<"sa[] ";
            for(int i=0;i<=n;i++)cout<<sa[i]<<" ";cout<<endl;
            cout<<"rank[] ";
            for(int i=0;i<=n;i++)cout<<rank[i]<<" ";cout<<endl;
            cout<<"height[] ";
            for(int i=0;i<=n;i++)cout<<height[i]<<" ";cout<<endl;
        }
    }DA;
    

    A.Mediocre String Problem (2018南京M,回文+LCP)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define pp pair<int,int>
    const ll mod=998244353;
    const int maxn=1e6+50;
    const ll inf=0x3f3f3f3f3f3f3f3fLL;
    int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
    int lcm(int a,int b){return a*b/gcd(a,b);}
    struct DA{
        #define F(x) ((x)/3+((x)%3==1?0:tb))
        #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
        int sa[maxn*20],rank[maxn*20],height[maxn*20],str[maxn*20];
        int wa[maxn*20],wb[maxn*20],wv[maxn*20],wss[maxn*20];
        int c0(int *r,int a,int b){
            return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];
        }
        int c12(int k,int *r,int a,int b){
            if(k==2)
                return r[a]<r[b]||(r[a]==r[b]&&c12(1,r,a+1,b+1));
            else return r[a]<r[b]||(r[a]==r[b]&&wv[a+1]<wv[b+1]);
        }
        void sort(int *r,int *a,int *b,int n,int m){
            int i;
            for(i=0;i<n;i++)wv[i]=r[a[i]];
            for(i=0;i<m;i++)wss[i]=0;
            for(i=0;i<n;i++)wss[wv[i]]++;
            for(i=1;i<m;i++)wss[i]+=wss[i-1];
            for(i=n-1;i>=0;i--)
                b[--wss[wv[i]]]=a[i];
        }
        void dc3(int *r,int *sa,int n,int m){
            int i,j,*rn=r+n;
            int *san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
            r[n]=r[n+1]=0;
            for(i=0;i<n;i++)if(i%3!=0)wa[tbc++]=i;
            sort(r+2,wa,wb,tbc,m);
            sort(r+1,wb,wa,tbc,m);
            sort(r,wa,wb,tbc,m);
            for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
                rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
            if(p<tbc)dc3(rn,san,tbc,p);
            else for(i=0;i<tbc;i++)san[rn[i]]=i;
            for(i=0;i<tbc;i++)if(san[i]<tb)wb[ta++]=san[i]*3;
            if(n%3==1)wb[ta++]=n-1;
            sort(r,wb,wa,ta,m);
            for(i=0;i<tbc;i++)wv[wb[i]=G(san[i])]=i;
            for(i=0,j=0,p=0;i<ta&&j<tbc;p++)
                sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
            for(;i<ta;p++)sa[p]=wa[i++];
            for(;j<tbc;p++)sa[p]=wb[j++];
        }
        void da(int n,int m){
            for(int i=n;i<n*3;i++)str[i]=0;
            dc3(str,sa,n+1,m);
            int i,j,k=0;
            for(i=0;i<=n;i++)rank[sa[i]]=i;
            for(i=0;i<n;i++){
                if(k)k--;
                j=sa[rank[i]-1];
                while(str[i+k]==str[j+k])k++;
                height[rank[i]]=k;
            }
        }
        void print(int n){
            cout<<"sa[] ";
            for(int i=0;i<=n;i++)cout<<sa[i]<<" ";cout<<endl;
            cout<<"rank[] ";
            for(int i=0;i<=n;i++)cout<<rank[i]<<" ";cout<<endl;
            cout<<"height[] ";
            for(int i=0;i<=n;i++)cout<<height[i]<<" ";cout<<endl;
        }
    }DA;
    struct PalTree{
        int next[maxn][26],fail[maxn],cnt[maxn],num[maxn],len[maxn],S[maxn],last,n,p;
        int newnode(int l){
            for(int i=0;i<26;i++)next[p][i]=0;
            cnt[p]=num[p]=0;len[p]=l;return p++;
        }
        void init(){
            p=0;newnode(0);newnode(-1);last=0;n=0;S[n]=-1;fail[0]=1;
        }
        int get_fail(int x){
            while(S[n-len[x]-1]!=S[n])x=fail[x];return x;
        }
        int add(int c){
            c-='a';S[++n]=c;int cur=get_fail(last);
            if(!next[cur][c]){
                int now=newnode(len[cur]+2);
                fail[now]=next[get_fail(fail[cur])][c];
                next[cur][c]=now;num[now]=num[fail[now]]+1;
            }
            last=next[cur][c];cnt[last]++;return num[last];
        }
        void count(){for(int i=p-1;i>=0;i--)cnt[fail[i]]+=cnt[i];}
    }PAM;
    char s[maxn],t[maxn];
    int num[maxn],cnt[maxn];
    int main()
    {
        std::ios::sync_with_stdio(false);
        std::cin.tie(0);
        std::cout.tie(0);
        cin>>s>>t;
        int lens=strlen(s),lent=strlen(t);
        int len=0;PAM.init();
        for(int i=0;i<lens;i++)DA.str[len++]=s[lens-i-1]-'a'+1,cnt[lens-i-1]=PAM.add(s[lens-i-1]);
        DA.str[len++]=30;
        for(int i=0;i<lent;i++)DA.str[len++]=t[i]-'a'+1;
        DA.str[len]=0;
        DA.da(len,200);
    	int p=DA.rank[lens+1];
    	//DA.print(len);
    	int now=len+1;
    	for(int i=p-1;i>=0;i--){
    		now=min(now,DA.height[i+1]);
    		if(DA.sa[i]>=0&&DA.sa[i]<lens){
    			num[lens-1-DA.sa[i]]=now;
    		}
    	}
    	now=len+1;
    	for(int i=p+1;i<=len;i++){
    		now=min(now,DA.height[i]);
    		if(DA.sa[i]>=0&&DA.sa[i]<lens){
    			num[lens-1-DA.sa[i]]=now;
    		}
    	}
    	ll ans=0;
        for(int i=0;i<lens-1;i++){
            ans+=1LL*num[i]*cnt[i+1];
        }
        cout<<ans<<endl;
        return 0;
    }
    

    I.POJ - 3415 Common Substrings

    单调栈优化

    题意

    求长度不小于K的公共子串的个数。

    题解

    对于一个LCP 贡献是len(lcp)-k+1

    我们可以将height进行分组,大于等于k的在同一组,如果两个后缀的最长公共子串>=k,那么它们肯定在同一个组内。现在从头开始扫,每遇到A的后缀时,就统计一下它和它前面的B的后缀能组成多少长度>=k的公共子串,然后再反过来处理B的后缀即可,一共需要扫两遍。但是这样的时间复杂度是O(n^2),是行不通的。

    因为两个后缀的LCP是它们之间的最小height值,所以可以维护一个自底向上递增的单调栈,如果有height值小于了当前栈顶的height值,那么大于它的那些只能按照当前这个小的值来计算。这样分别处理两次,一次处理A的后缀,一次处理B的后缀。需要记录好栈里的和以及每个组内的后缀数。

    注意longlong

    18.I1

    #include<string>
    #include<iostream>
    #include<iosfwd>
    #include<cmath>
    #include<cstring>
    #include<stdlib.h>
    #include<stdio.h>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    #define pp pair<int,int>
    const ll mod=998244353;
    const int maxn=2e5+50;
    const ll inf=0x3f3f3f3f3f3f3f3fLL;
    int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
    int lcm(int a,int b){return a*b/gcd(a,b);}
    struct DA{
        #define F(x) ((x)/3+((x)%3==1?0:tb))
        #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
        int sa[maxn*10],rank[maxn*10],height[maxn*10],str[maxn*10];
        int wa[maxn*10],wb[maxn*10],wv[maxn*10],wss[maxn*10];
        int RMQ[maxn],mm[maxn],best[20][maxn];;
        int c0(int *r,int a,int b){
            return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];
        }
        int c12(int k,int *r,int a,int b){
            if(k==2)
                return r[a]<r[b]||(r[a]==r[b]&&c12(1,r,a+1,b+1));
            else return r[a]<r[b]||(r[a]==r[b]&&wv[a+1]<wv[b+1]);
        }
        void sort(int *r,int *a,int *b,int n,int m){
            int i;
            for(i=0;i<n;i++)wv[i]=r[a[i]];
            for(i=0;i<m;i++)wss[i]=0;
            for(i=0;i<n;i++)wss[wv[i]]++;
            for(i=1;i<m;i++)wss[i]+=wss[i-1];
            for(i=n-1;i>=0;i--)
                b[--wss[wv[i]]]=a[i];
        }
        void dc3(int *r,int *sa,int n,int m){
            int i,j,*rn=r+n;
            int *san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
            r[n]=r[n+1]=0;
            for(i=0;i<n;i++)if(i%3!=0)wa[tbc++]=i;
            sort(r+2,wa,wb,tbc,m);
            sort(r+1,wb,wa,tbc,m);
            sort(r,wa,wb,tbc,m);
            for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
                rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
            if(p<tbc)dc3(rn,san,tbc,p);
            else for(i=0;i<tbc;i++)san[rn[i]]=i;
            for(i=0;i<tbc;i++)if(san[i]<tb)wb[ta++]=san[i]*3;
            if(n%3==1)wb[ta++]=n-1;
            sort(r,wb,wa,ta,m);
            for(i=0;i<tbc;i++)wv[wb[i]=G(san[i])]=i;
            for(i=0,j=0,p=0;i<ta&&j<tbc;p++)
                sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
            for(;i<ta;p++)sa[p]=wa[i++];
            for(;j<tbc;p++)sa[p]=wb[j++];
        }
        void da(int n,int m){
            for(int i=n;i<n*3;i++)str[i]=0;
            dc3(str,sa,n+1,m);
            int i,j,k=0;
            for(i=0;i<=n;i++)rank[sa[i]]=i;
            for(i=0;i<n;i++){
                if(k)k--;
                j=sa[rank[i]-1];
                while(str[i+k]==str[j+k])k++;
                height[rank[i]]=k;
            }
        }
    
        void initRMQ(int n){
            for(int i=1;i<=n;i++)RMQ[i]=height[i];
            mm[0]=-1;
            for(int i=1;i<=n;i++)
                mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
            for(int i=1;i<=n;i++)best[0][i]=i;
            for(int i=1;i<=mm[n];i++)
            for(int j=1;j+(1<<i)-1<=n;j++){
                int a=best[i-1][j];
                int b=best[i-1][j+(1<<(i-1))];
                if(RMQ[a]<RMQ[b])best[i][j]=a;
                else best[i][j]=b;
            }
        }
        int askRMQ(int a,int b){
            int t;
            t=mm[b-a+1];
            b-=(1<<t)-1;
            a=best[t][a];b=best[t][b];
            return RMQ[a]<RMQ[b]?a:b;
        }
        int lcp(int a,int b){
            a=rank[a];b=rank[b];
            if(a>b)swap(a,b);
            return height[askRMQ(a+1,b)];
        }
        void print(int n){
            cout<<"sa[] ";
            for(int i=0;i<=n;i++)cout<<sa[i]<<" ";cout<<endl;
            cout<<"rank[] ";
            for(int i=0;i<=n;i++)cout<<rank[i]<<" ";cout<<endl;
            cout<<"height[] ";
            for(int i=0;i<=n;i++)cout<<height[i]<<" ";cout<<endl;
        }
    }DA;
    char str[maxn],str1[maxn];
    int s[maxn][2];
    int main()
    {
        std::ios::sync_with_stdio(false);
        std::cin.tie(0);
        std::cout.tie(0);
        int k;
        while(cin>>k&&k){
            cin>>str>>str1;
            int lena=strlen(str);
            int lenb=strlen(str1);
            int n=0;
            for(int i=0;i<lena;i++)DA.str[n++]=str[i];
            DA.str[n++]=1;
            for(int i=0;i<lenb;i++)DA.str[n++]=str1[i];
            DA.da(n,300);
            ll tot=0,top=0;
            ll sum=0;
            for(int i=1;i<=n;i++){
                if(DA.height[i]<k)top=0,tot=0;
                else{
                    ll cnt=0;
                    if(DA.sa[i-1]<lena){
                        cnt++;tot+=(ll)DA.height[i]-k+1;
                    }
                    while(top&&DA.height[i]<=s[top-1][0]){
                        top--;
                        tot+=(ll)s[top][1]*(DA.height[i]-s[top][0]);
                        cnt+=(ll)s[top][1];
                    }
                    s[top][0]=DA.height[i];s[top++][1]=cnt;
                    if(DA.sa[i]>lena)sum+=tot;
                }
            }
            tot=top=0;
            for(int i=1;i<=n;i++){
                if(DA.height[i]<k)top=0,tot=0;
                else{
                    int cnt=0;
                    if(DA.sa[i-1]>lena){
                        cnt++;tot+=(ll)DA.height[i]-k+1;
                    }
                    while(top&&DA.height[i]<=s[top-1][0]){
                        top--;
                        tot+=(ll)s[top][1]*(DA.height[i]-s[top][0]);
                        cnt+=(ll)s[top][1];
                    }
                    s[top][0]=DA.height[i];s[top++][1]=cnt;
                    if(DA.sa[i]<lena)sum+=tot;
                }
            }
            cout<<sum<<endl;
        }
        return 0;
    }
    
  • 相关阅读:
    Android开发技术周报 Issue#39
    Android开发技术周报 Issue#40
    Android开发技术周报 Issue#41
    js删除最后一个字符串方法
    Firefox 插件 JSview是一套比较实用的JS,CSS文件查看工具,很方便,很快捷地查看页面引用了哪些文件,作为Web前端开发者是一套必备的插件,由于Firefox升级过快,插件很快不兼容了,这里对插件做了一些调整,可以兼容最新Firefox浏览器(目前FireFox 21)
    IT人常用的网站
    Jquery,全选,反选,
    Vue.js基础(二)
    Vue.js基础(一)
    Vue.js模拟百度下拉框
  • 原文地址:https://www.cnblogs.com/luowentao/p/10332298.html
Copyright © 2011-2022 走看看