• 后缀数组


    luogu回文串

    题意给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两个子串中有一个位置不同。

    首先我们知道可以用马拉车算法算出该字符串然后再用height来求出答案

    但是如果暴力的话,肯定不行,所以我们运用马拉车的一个性质

        for(int i=1;i<len;++i) {
            if(mx>i) p[i]=min(p[id*2-i],mx-i);
            else p[i]=1;
            while(ms[i+p[i]]==ms[i-p[i]]) {
                ++p[i]; 
                if(i+p[i]>mx) {
                    //只有在这里突破了mx的子串才是之前没有出现过的回文子串.
                    //算入分隔符#的限制的话.
                    calc(i-p[i]+1,i+p[i]-1);
                }
            }
            if(p[i]+i>mx) {
                mx=i+p[i],id=i;
            }
        }   
    

      

    实测发现,Manacher算法的此类拓展不一定提交本质不同的回文子串(不算#),因为会有#的限制,

    但是结合Manacher算法对称性(关于id对称)的原理可以发现,不在此类拓展中提交(calc)的子串一定在之前提到过.

    而Manacher总复杂度O(n),所以是完全能够接受的.

    然后问题就换成了如何统计一个回文子串(也就是一个子串)的出现次数.

    这里使用后缀数组解决.

    一个子串是某一个后缀的前缀,因而,如果一个子串在K个后缀中出现过,那么该子串一定是这K个后缀Lcs的前缀.

    也就有len(LCS)>=len(子串)

    易知这K个后缀排序后构成连续的一段,所以可以用后缀数组解决.

    每次在Height数组上二分,向前后分别拓展,rmq查询判断Lcs长度是否满足要求即可. 总复杂度O(nlogn)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<stack>
    using namespace std;
    typedef long long ll;
    const ll inf=0x3f3f3f3f;
    const ll M=530000;
    
    ll a[M],b[M],rk[M],sa[M],height[M],x[M],y[M],c[M],id[M],p,top,st[M];
    ll n,m,vis[M],maxy=99999;
    ll temp,s,len1,len2,ans;
    struct nod
    {
        ll height,s;
    }node;
    stack<nod>stk;
    void get_sa()
    {
        for(ll i=1;i<=n;i++) ++c[x[i]=b[i]];
        for(ll i=2;i<=m;i++) c[i]+=c[i-1];
        for(ll i=n;i>=1;--i) sa[c[x[i]]--]=i;
        for(ll k=1;k<=n;k<<=1){
            ll num=0;
            for(ll i=n-k+1;i<=n;i++) y[++num]=i;
            for(ll i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k;
            for(ll i=1;i<=m;i++) c[i]=0;
            for(ll i=1;i<=n;i++) ++c[x[i]];
            for(ll i=2;i<=m;i++) c[i]+=c[i-1];
            for(ll i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
            swap(x,y);
            x[sa[1]]=1;
            num=1;
            for(ll i=2;i<=n;i++)
                x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
                if(num==n) break;
                m=num;
        }
    }
    void get_height()
    {
        ll k=0,j;
        for(ll i=1;i<=n;i++) rk[sa[i]]=i;
        for(ll i=1;i<=n;i++){
            if(rk[i]==1) continue;
            if(k)k--;
            j=sa[rk[i]-1];
            while(j+k<=n&&i+k<=n&&b[i+k]==b[j+k]) k++;
            height[rk[i]]=k;
        }
    }
    void solve()
    {
        for(ll i=1;i<=n;i++){
            s=0;
            while(stk.size()>0&&height[i]<stk.top().height){
                s+=stk.top().s;
                temp+=(height[i]-stk.top().height)*stk.top().s;
                stk.pop();
            }
            if(sa[i-1]<=len1) temp+=height[i],s++;
            if(sa[i]>len1) ans+=temp;
            node.height=height[i];node.s=s;
            stk.push(node);
        }
        while(stk.size()>0) stk.pop();
        for(ll i=1;i<=n;i++){
            s=0;
            while(stk.size()>0&&height[i]<stk.top().height){
                s+=stk.top().s;
                temp+=(height[i]-stk.top().height)*stk.top().s;
                stk.pop();
            }
            if(sa[i-1]>len1) temp+=height[i],s++;
            if(sa[i]<=len1) ans+=temp;
            node.height=height[i];node.s=s;
            stk.push(node);
        }
        printf("%lld\n",ans);
    }
    int main()
    {
        char s1[200005],s2[200005];
        scanf("%s",s1);
        scanf("%s",s2);
        len1=strlen(s1),len2=strlen(s2);
        for(ll i=1;i<=len1;i++) b[i]=s1[i-1];
        b[len1+1]=666;
        for(ll i=1;i<=len2;i++) b[len1+1+i]=s2[i-1];
        n=len1+len2+1;
        m=777;
        get_sa();
        get_height();
        solve();
    }
    

     luogu喵星人点名

    这是一道后缀数组+莫队的题
    题意,给出每个单位一个姓与名,然后询问的字符串里某单单位的性或名的子串,
    则称该单位被询问了一次,第一问,每个询问问到了多少个单位,下文解释到这是个区间不同颜色,
    第二问:每只单位出现次数
    如果一个串是某些后缀的前缀,那么这些后缀排名肯定是连续的,所以在sa数组上连续区间这时,我们在二分出这个区间,再看看这个区间有多少个颜色,也就是多少个单位,这就用莫队回答了第一个问
    对于第二问:可以进行差分
    当莫队在处理第j个询问中发现一个新出现的颜色i时,num[i]+=cnt-j+1(最大可能总数);cnt是总询问数。

    当莫队在处理第j个询问中发现少了一个颜色i时,num[i]−=cnt−j+1(也就是此刻没了后面的颜色询问这个单位是没有占到的)

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    inline int gi() {
        register int data = 0, w = 1;
        register char ch = 0;
        while (!isdigit(ch) && ch != '-') ch = getchar();
        if (ch == '-') w = -1, ch = getchar();
        while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar();
        return w * data;
    }
    const int MAX_N = 2e5 + 5;
    int N, n, m, a[MAX_N], id[MAX_N], lim = 1e4 + 5;
    int sa[MAX_N];
    void GetSA() {
    #define cmp(i, j, k) (y[i] == y[j] && y[i + k] == y[j + k])
        static int x[MAX_N], y[MAX_N], bln[MAX_N];
        int M = lim;
        for (int i = 1; i <= N; i++) bln[x[i] = a[i]]++;
        for (int i = 1; i <= M; i++) bln[i] += bln[i - 1];
        for (int i = N; i >= 1; i--) sa[bln[x[i]]--] = i;
        for (int k = 1; k <= N; k <<= 1) {
            int p = 0;
            for (int i = 0; i <= M; i++) y[i] = 0;
            for (int i = N - k + 1; i <= N; i++) y[++p] = i;
            for (int i = 1; i <= N; i++) if (sa[i] > k) y[++p] = sa[i] - k;
            for (int i = 0; i <= M; i++) bln[i] = 0;
            for (int i = 1; i <= N; i++) bln[x[y[i]]]++;
            for (int i = 1; i <= M; i++) bln[i] += bln[i - 1];
            for (int i = N; i >= 1; i--) sa[bln[x[y[i]]]--] = y[i];
            swap(x, y); x[sa[1]] = p = 1;
            for (int i = 2; i <= N; i++) x[sa[i]] = cmp(sa[i], sa[i - 1], k) ? p : ++p;
            if (p >= N) break;
            M = p;
        }
    }
    const int LEN = 400;
    int cnt[MAX_N], bln[MAX_N];
    struct Query { int l, r, id; } q[MAX_N]; int q_cnt = 0;
    //inline bool operator < (const Query &a, const Query &b) {
    //    if (bln[a.l] ^ bln[b.l]) return a.r < b.r;
    //    else return (bln[a.l] & 1) ? a.r < b.r : a.r > b.r;
    //}
    int CMP(Query a,Query b)
    {
        if(bln[a.l]==bln[b.l] ) return a.r<b.r;
        else return bln[a.l]<bln[b.l];
    }
    int A1, ans1[MAX_N], ans2[MAX_N];
    
    void add(int x, int pos){
        cnt[id[sa[x]]]++;
        if (cnt[id[sa[x]]] == 1) ++A1, ans2[id[sa[x]]] += q_cnt - pos + 1;
    }
    void del(int x, int pos){
        cnt[id[sa[x]]]--;
        if (cnt[id[sa[x]]] == 0) --A1, ans2[id[sa[x]]] -= q_cnt - pos + 1;
    }
    int main () {
    //#ifndef ONLINE_JUDGE
    //    freopen("cpp.in", "r", stdin);
    //#endif
        n = gi(), m = gi();
        for (int i = 1, L; i <= n; i++) {
            L = gi(); for (int j = 1; j <= L; j++) a[++N] = gi(), id[N] = i; a[++N] = ++lim;
            L = gi(); for (int j = 1; j <= L; j++) a[++N] = gi(), id[N] = i; a[++N] = ++lim;
        }
        GetSA();
        for (int i = 1; i <= m; i++) {///首先就是两个while可以求出一个二分的最大左右区间,
            int L = 1, R = N;
            for (int len = gi(), j = 1; j <= len; ++j) {
                int x = gi(), l = L, r = R;
                while (l <= r) {
                    int mid = (l + r) >> 1;
                    if (a[sa[mid] + j - 1] < x) l = mid + 1;
                    else r = mid - 1;
                }
                int tmp = l; l = L, r = R;
                while (l <= r) {
                    int mid = (l + r) >> 1;
                    if (a[sa[mid] + j - 1] <= x) l = mid + 1;
                    else r = mid - 1;
                }
                L = tmp, R = r;
            }
            if (L <= R) q[++q_cnt] = (Query){L, R, i};
        }
        for (int i = 1; i <= N; i++) bln[i] = (i - 1) / LEN + 1;
        sort(q+1, q+1+q_cnt,CMP);
        for (int ql = 1, qr = 0, i = 1; i <= q_cnt; i++) {
            while (ql < q[i].l) del(ql, i), ++ql;
            while (ql > q[i].l) --ql, add(ql, i);
            while (qr < q[i].r) ++qr, add(qr, i);
            while (qr > q[i].r) del(qr, i), --qr;
            ans1[q[i].id] = A1;
        }
        for (int i = 1; i <= m; i++) printf("%d\n", ans1[i]);
        for (int i = 1; i <= n; i++) printf("%d ", ans2[i]);
        return 0;
        }
    }
    

      luogu找相同字符串

    差分就完事

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<stack>
    using namespace std;
    typedef long long ll;
    const ll inf=0x3f3f3f3f;
    const ll M=530000;
    
    ll a[M],b[M],rk[M],sa[M],height[M],x[M],y[M],c[M],id[M],p,top,st[M];
    ll n,m,vis[M],maxy=99999;
    ll temp,s,len1,len2,ans;
    struct nod
    {
        ll height,s;
    }node;
    stack<nod>stk;
    void get_sa()
    {
        for(ll i=1;i<=n;i++) ++c[x[i]=b[i]];
        for(ll i=2;i<=m;i++) c[i]+=c[i-1];
        for(ll i=n;i>=1;--i) sa[c[x[i]]--]=i;
        for(ll k=1;k<=n;k<<=1){
            ll num=0;
            for(ll i=n-k+1;i<=n;i++) y[++num]=i;
            for(ll i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k;
            for(ll i=1;i<=m;i++) c[i]=0;
            for(ll i=1;i<=n;i++) ++c[x[i]];
            for(ll i=2;i<=m;i++) c[i]+=c[i-1];
            for(ll i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
            swap(x,y);
            x[sa[1]]=1;
            num=1;
            for(ll i=2;i<=n;i++)
                x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
                if(num==n) break;
                m=num;
        }
    }
    void get_height()
    {
        ll k=0,j;
        for(ll i=1;i<=n;i++) rk[sa[i]]=i;
        for(ll i=1;i<=n;i++){
            if(rk[i]==1) continue;
            if(k)k--;
            j=sa[rk[i]-1];
            while(j+k<=n&&i+k<=n&&b[i+k]==b[j+k]) k++;
            height[rk[i]]=k;
        }
    }
    void solve()
    {
        for(ll i=1;i<=n;i++){
            s=0;
            while(stk.size()>0&&height[i]<stk.top().height){
                s+=stk.top().s;
                temp+=(height[i]-stk.top().height)*stk.top().s;
                stk.pop();
            }
            if(sa[i-1]<=len1) temp+=height[i],s++;
            if(sa[i]>len1) ans+=temp;
            node.height=height[i];node.s=s;
            stk.push(node);
        }
        while(stk.size()>0) stk.pop();
        for(ll i=1;i<=n;i++){
            s=0;
            while(stk.size()>0&&height[i]<stk.top().height){
                s+=stk.top().s;
                temp+=(height[i]-stk.top().height)*stk.top().s;
                stk.pop();
            }
            if(sa[i-1]>len1) temp+=height[i],s++;
            if(sa[i]<=len1) ans+=temp;
            node.height=height[i];node.s=s;
            stk.push(node);
        }
        printf("%lld\n",ans);
    }
    int main()
    {
        char s1[200005],s2[200005];
        scanf("%s",s1);
        scanf("%s",s2);
        len1=strlen(s1),len2=strlen(s2);
        for(ll i=1;i<=len1;i++) b[i]=s1[i-1];
        b[len1+1]=666;
        for(ll i=1;i<=len2;i++) b[len1+1+i]=s2[i-1];
        n=len1+len2+1;
        m=777;
        get_sa();
        get_height();
        solve();
    }
    

     luogu dna

    考虑从A AA串的第i ii为匹配,找到一个最长的前缀与B BB匹配,然后跳过这一位,继续匹配下去。
    如果跳过字符超过3 33个,就是不合法的。
    可以把B BB串加入到A AA串后面跑后缀数组,就可以快速求出最长匹配的位置。
    https://blog.csdn.net/liangzihao1/article/details/87640326

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <cstring>
    
    const int maxn=2e5+7;
    
    using namespace std;
    
    int T,n,m,len,ans;
    int x[maxn],y[maxn],c[maxn],lg[maxn];
    char s[maxn],s1[maxn];
    
    struct suffle_array{
        int rank[maxn],sa[maxn],h[maxn][20];
        void getsa()
        {
            memset(x,0,sizeof(x));
            memset(y,0,sizeof(y));
            memset(c,0,sizeof(c));
            memset(rank,0,sizeof(rank));
            memset(sa,0,sizeof(sa));
            memset(h,0,sizeof(h));
            int n=len,m=1000;
            for (int i=1;i<=m;i++) c[i]=0;
            for (int i=1;i<=n;i++) x[i]=s[i];
            for (int i=1;i<=n;i++) c[x[i]]++;
            for (int i=1;i<=m;i++) c[i]+=c[i-1];
            for (int i=n;i>0;i--) sa[c[x[i]]--]=i;
            for (int k=1;k<=n;k<<=1)
            {
                int num=0;
                for (int i=n-k+1;i<=n;i++) y[++num]=i;
                for (int i=1;i<=n;i++) if (sa[i]>k) y[++num]=sa[i]-k;
                for (int i=1;i<=m;i++) c[i]=0;
                for (int i=1;i<=n;i++) c[x[i]]++;
                for (int i=1;i<=m;i++) c[i]+=c[i-1];
                for (int i=n;i>0;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
                swap(x,y);
                num=1;
                x[sa[1]]=1;
                for (int i=2;i<=n;i++)
                {
                    if ((y[sa[i]]!=y[sa[i-1]]) || (y[sa[i]+k]!=y[sa[i-1]+k]))
                    {
                        x[sa[i]]=++num;
                    }
                    else x[sa[i]]=num;
                }
                if (num>=n) break;
                m=num;
            }
            for (int i=1;i<=n;i++) rank[i]=x[i];
        }
        void getheight()
        {
            int n=len,k=0;		
            for (int i=1;i<=n;i++)
            {
                if (k) k--;
                int j=sa[rank[i]-1];
                while ((i+k<=n) && (j+k<=n) && (s[i+k]==s[j+k])) k++;
                h[rank[i]][0]=k;
            }
            int c=1;
            for (int j=1;j<20;j++)
            {
                for (int i=1;i+2*c-1<=n;i++) h[i][j]=min(h[i][j-1],h[i+c][j-1]);
                c<<=1;
            }
        }
        int lcp(int x,int y)
        {
            x=rank[x],y=rank[y];
            if (x>y) swap(x,y);
            x++;
            int c=lg[y-x+1];
            return min(h[x][c],h[y-(1<<c)+1][c]);
        }
    }A;
    
    int main()
    {
        for (int i=1;i<=2e5;i++) lg[i]=trunc(log(i+0.5)/log(2));	
        scanf("%d",&T);
        while (T--)
        {
            scanf("%s",s+1);
            n=strlen(s+1);
            scanf("%s",s1+1);
            m=strlen(s1+1);
            s[n+1]=' ';
            for (int i=1;i<=m;i++) s[n+1+i]=s1[i];		
            len=n+m+1;
            A.getsa(),A.getheight();
            ans=0;		
            for (int i=1;i<=n-m+1;i++)
            {
                int num=0,flag=0;					
                while ((flag<=3) && (num<m))
                {
                    num+=A.lcp(i+num,n+2+num);
                    if (num>=m) break;
                    flag++,num++;
                }
                if (flag<=3) ans++;
            }
            printf("%d\n",ans);
        }
    }
    

     luogu 生成魔咒

    这道题还是比较好的; 这里提供后缀数组的解法。 要求出每一个前缀本质不同的后缀的个数
    ,那么我们可以把原序列倒过来,然后实际上就是对于每一个后缀求与其它后缀不重复的前缀
    个数,也即是后缀长度减去height值; 求出某一个后缀对答案的贡献之后,他不应该停留在
    元序列中对后续答案的求解产生影响,
    所以应该把它删除; 在实现方式上,可以使用链表,与平衡树的操作有些类似
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    #define ll long long
    const int maxn=1e6+10;
    const int inf=1e9;
    
    int n,m=200,x[maxn],y[maxn],c[maxn],sa[maxn],rnk[maxn],height[maxn];
    int s[maxn];
    
    void build_sa()
    {
        for (int i=0; i<m; i++) c[i]=0;
        for (int i=0; i<n; i++) c[x[i]=s[i]]++;
        for (int i=1; i<m; i++) c[i]+=c[i-1];
        for (int i=n-1; i>=0; i--) sa[--c[x[i]]]=i;
    
        for (int k=1; k<=n; k<<=1)
        {
            int p=0;
            for (int i=n-k; i<n; i++) y[p++]=i;
            for (int i=0; i<n; i++) if (sa[i]>=k) y[p++]=sa[i]-k;
    
            for (int i=0; i<m; i++) c[i]=0;
            for (int i=0; i<n; i++) c[x[i]]++;
            for (int i=1; i<m; i++) c[i]+=c[i-1];
            for (int i=n-1; i>=0; i--) sa[--c[x[y[i]]]]=y[i];
    
            swap(x,y);
            p=1; x[sa[0]]=0;
            for (int i=0; i<n; i++)
                x[sa[i]] = y[sa[i-1]]==y[sa[i]] && ((sa[i-1]+k>=n?-1:y[sa[i-1]+k])==(sa[i]+k>=n?-1:y[sa[i]+k])) ?p-1:p++;
            if (p>n) break;
            m=p;
        }
    }
    
    void build_height()
    {
        for (int i=0; i<n; i++) rnk[sa[i]]=i;
        int k=0; height[0]=0;
        for (int i=0; i<n; i++)
        {
            if (!rnk[i]) continue;
            if (k) k--;
            int j=sa[rnk[i]-1];
            while (i+k<n && j+k<n && s[i+k]==s[j+k]) k++;
            height[rnk[i]]=k;
        }
    }
    
    void debug(int *A,int len) {for (int i=0; i<len; i++) printf("%d ",A[i]);}
    
    int tmp[maxn],a[maxn],pre[maxn],next[maxn];
    ll ans[maxn];
    int main()
    {
        scanf("%d",&n);
        for (int i=0; i<n; i++) scanf("%d",&a[i]);
        for (int i=0; i<n; i++) tmp[i]=a[i];
        sort(tmp,tmp+n);
        int nn=unique(tmp,tmp+n)-tmp;
        for (int i=0; i<n; i++)
            a[i]=lower_bound(tmp,tmp+nn,a[i])-tmp+1;
        for (int i=0; i<n; i++) s[i]=a[n-i-1];
        build_sa();
        build_height();
    
        for (int i=0; i<n-1; i++) next[i]=i+1;
        for (int i=1; i<n; i++) pre[i]=i-1;
        for (int i=0; i<n; i++){
            int rk=rnk[i];
            int now=n-i-max(height[rk],height[next[rk]]);///这部分重叠的在后面没找到重复的时候会算进去的
            ///然后就是这个字符开始的最长前缀就在这两个之间
            ans[i]=(ll)now;
            height[next[rk]]=min(height[next[rk]],height[rk]);///然后就是这个点你就要删掉了,那么这个点有关的height要取最小值,因为这个点没了,如果这个sa【i】和sa【i-1】更短,那删除完i肯定继承这点过来
            height[rk]=0;
            if (rk) next[pre[rk]]=next[rk];//然后i点的前个点的下个点等于这个点的下个点
            pre[next[rk]]=pre[rk];///这个点的下个点的前个点等于这个点的前个点。就和链表那个一模一样
        }
        for (int i=n-1; i>=0; i--) ans[i]+=ans[i+1];
        for (int i=n-1; i>=0; i--) printf("%lld\n",ans[i]);
        return 0;
    }
    

     

    https://blog.csdn.net/queuelovestack/article/details/53031731

    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<queue>
    #include<stack>
    #include<math.h>
    #include<vector>
    #include<map>
    #include<set>
    #include<list>
    #include<bitset>
    #include<cmath>
    #include<complex>
    #include<string>
    #include<algorithm>
    #include<iostream>
    #define eps 1e-9
    #define LL long long
    #define PI acos(-1.0)
    #define bitnum(a) __builtin_popcount(a)
    using namespace std;
    const int N = 5005;
    const int M = 100005;
    const int inf = 1000000007;
    const int mod = 1000000007;
    const int MAXN = 50005;
    int n,m,height[MAXN],sa[MAXN],c[MAXN],rk[MAXN],x[MAXN],s[MAXN],y[MAXN];
    void get_sa()
    {
        for(int i=1;i<=m;i++) c[i]=0;
        for(int i=1;i<=n;i++) ++c[x[i]=s[i]];
        for(int i=2;i<=m;i++) c[i]+=c[i-1];
        for(int i=n;i>=1;--i) sa[c[x[i]]--]=i;
        for(int k=1;k<=n;k<<=1){
            int num=0;
            for(int i=n-k+1;i<=n;i++) y[++num]=i;
            for(int i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k;
            for(int i=1;i<=m;i++) c[i]=0;
            for(int i=1;i<=n;i++) ++c[x[i]];
            for(int i=2;i<=m;i++) c[i]+=c[i-1];
            for(int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
            swap(x,y);
            x[sa[1]]=1;
            num=1;
            for(int i=2;i<=n;i++)
                x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
                if(num==n) break;
                m=num;
        }
    }
    void get_height()
    {
        int k=0,j;
        for(int i=1;i<=n;i++) rk[sa[i]]=i;
        for(int i=1;i<=n;i++){
            if(rk[i]==1) continue;
            if(k)k--;
            j=sa[rk[i]-1];
            while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k]) k++;
            height[rk[i]]=k;
        }
    }
    int minnum[MAXN][16];
    void RMQ()   		//预处理  O(nlogn)
    {
    	int i,j;
    	int m=(int)(log(n*1.0)/log(2.0));
    	for(i=1;i<=n;i++)
    		minnum[i][0]=height[i];
    	for(j=1;j<=m;j++)
    		for(i=1;i+(1<<j)-1<=n;i++)
    			minnum[i][j]=min(minnum[i][j-1],minnum[i+(1<<(j-1))][j-1]);
    }
    int Ask_MIN(int a,int b) 	//O(1)
    {
    	int k=int(log(b-a+1.0)/log(2.0));
    	return min(minnum[a][k],minnum[b-(1<<k)+1][k]);
    }
    int calprefix(int a,int b)
    {
        a=rk[a],b=rk[b];
        if(a>b)
            swap(a,b);
        return Ask_MIN(a+1,b);///因为height是i-1和i的公共前缀,所以我们得+1防止越界
    }
    char p[5];
    int main()
    {
        int t,i,j,k,ans,Max,q;
        scanf("%d",&t);
        while(t--)
        {
            Max=1;
            m=300;
            q=0;
            scanf("%d",&n);
            for(i=0;i<n;i++)
            {
                scanf("%s",p);
                s[++q]=p[0];
            }
    //        s[++q]='\0';
            get_sa();
            get_height();
            RMQ();
            for(i=1;i<=n;i++)
            {
                for(j=1;j+i<=n;j+=i)///噔噔
                {
                    ans=calprefix(j,j+i);///寻找公共后缀
    //                printf("x=%d\n",ans);
                    k=j-(i-ans%i);//噔噔
                    ans=ans/i+1;
                    if(k>=0&&calprefix(k,k+i)>=i)
                        ans++;
                    Max=max(Max,ans);
                }
            }
            printf("%d\n",Max);
        }
        return 0;
    }
    

      

  • 相关阅读:
    iOS项目之自定义斜向文字标签
    iOS进阶之两个模型数组的去重方法
    iOS进阶之正则表达式
    iOS项目之使用开关控制日志输出的功能
    iOS进阶之UDP代理鉴权过程
    990元外贸企业建站方案
    ¥990起,性价比最高的建站服务。
    PHP调用WEBSERVICE接口常见问题答疑以及总结
    Python消息队列工具 Python-rq 中文教程
    电商网站的用户停留时间越长越好吗?我看未必。
  • 原文地址:https://www.cnblogs.com/hgangang/p/11680305.html
Copyright © 2020-2023  润新知