Processing math: 100%


UOJ#219/BZOJ4650 [NOI2016]优秀的拆分 字符串 SA ST表

原文链接http://www.cnblogs.com/zhouzhendong/p/9025092.html

题目传送门 - UOJ#219 (推荐,题面清晰)

题目传送门 - BZOJ4650

题意

  如果一个字符串可以被拆分为AABB的形式,其中AA和BB是任意非空字符串,则我们称该字符串的这种拆分是优秀的。

  现在给出一个长度为n的字符串S,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。

  多组数据,数据组数10,n30000

题解

  考虑先处理AA类型的字符串,对于每一个位置i,分别处理以i位置开头或结尾的AA类型字符串个数。

  如果完成了这个处理,那么最后的统计便毫不费力。(注意开longlong)

  考虑如何处理。

  首先,枚举A串的长度,记为L。我们设置一些关键点,依次为1L,2L,3L,,iL,(i+1)L,

  考虑处理相邻两个关键点对答案的贡献。对于第i对相邻的关键点,我们需要得到一个特殊的最长公共子串。

  这个最长公共子串由结尾为iL,(i+1)L的长度不大于L的最长公共后缀(LCS) 和 开头为iL,(i+1)L的长度不大于L的最长公共前缀(LCP) 拼接而成。

  于是你可以通过删除一些这个最长公共子串的后缀和前缀,使得包含两个关键点的相同串首尾相接,来得到长度为2LAA类型字符串。

  这样的串对于一开始要处理的那个东西有一个区间的贡献。可以用差分来搞定。

  至于求LCSLCP,我们只需要先后缀数组+ST表预处理一下,就可以了。

  时间复杂度O(nlogn)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <bits/stdc++.h>
#define rank r_a_n_k
using namespace std;
typedef long long LL;
const int N=60005;
int T,n,m,tmp[N],SA[N],rank[N],tax[N],height[N];
int ST[N][18],A[N],B[N];
char s[N];
void Sort(int n){
    for (int i=0;i<=m;i++)
        tax[i]=0;
    for (int i=1;i<=n;i++)
        tax[rank[i]]++;
    for (int i=1;i<=m;i++)
        tax[i]+=tax[i-1];
    for (int i=n;i>=1;i--)
        SA[tax[rank[tmp[i]]]--]=tmp[i];
}
bool cmp(int rk[],int x,int y,int w){
    return rk[x]==rk[y]&&rk[x+w]==rk[y+w];
}
void Suffix_Array(char s[],int n){
    memset(SA,0,sizeof SA);
    memset(tmp,0,sizeof tmp);
    memset(rank,0,sizeof rank);
    memset(height,0,sizeof height);
    for (int i=1;i<=n;i++)
        rank[i]=s[i],tmp[i]=i;
    m=127;
    Sort(n);
    for (int w=1,p=0;p<n;w<<=1,m=p){
        p=0;
        for (int i=n-w+1;i<=n;i++)
            tmp[++p]=i;
        for (int i=1;i<=n;i++)
            if (SA[i]>w)
                tmp[++p]=SA[i]-w;
        Sort(n);
        swap(tmp,rank);
        rank[SA[1]]=p=1;
        for (int i=2;i<=n;i++)
            rank[SA[i]]=cmp(tmp,SA[i],SA[i-1],w)?p:++p;
    }
    for (int i=1,j,k=0;i<=n;height[rank[i++]]=k)
        for (k=max(k-1,0),j=SA[rank[i]-1];s[i+k]==s[j+k];k++);
}
void GetST(int n){
    memset(ST,0,sizeof ST);
    for (int i=1;i<=n;i++){
        ST[i][0]=height[i];
        for (int j=1;j<18;j++){
            ST[i][j]=ST[i][j-1];
            if (i-(1<<(j-1))>0)
                ST[i][j]=min(ST[i][j],ST[i-(1<<(j-1))][j-1]);
        }
    }
}
int QueryST(int L,int R){
    int val=floor(log(R-L+1)/log(2));
    return min(ST[L+(1<<val)-1][val],ST[R][val]);
}
int LCP(int i,int j){
    i=rank[i],j=rank[j];
    return QueryST(min(i,j)+1,max(i,j));
}
int LCS(int i,int j){
    return LCP(n*2-i+2,n*2-j+2);
}
int main(){
    scanf("%d",&T);
    while (T--){
        memset(s,0,sizeof s);
        scanf("%s",s+1);
        n=strlen(s+1);
        s[n+1]='#';
        for (int i=n;i>=1;i--)
            s[n*2-i+2]=s[i];
        Suffix_Array(s,n*2+1);
        GetST(n*2+1);
        memset(A,0,sizeof A);
        memset(B,0,sizeof B);
        for (int L=1;L<=n;L++)
            for (int i=1;i<=n/L-1;i++){
                int lcp=min(LCP(L*i,L*(i+1)),L);
                int lcs=min(LCS(L*i,L*(i+1)),L);
                if (lcp+lcs<=L)
                    continue;
                int l=i*L-lcs+1,r=(i+1)*L+lcp-1;
                int cov=lcp+lcs-(L+1);
                B[l]++,B[l+cov+1]--;
                A[r-cov]++,A[r+1]--;
            }
        for (int i=1;i<=n;i++)
            A[i]+=A[i-1],B[i]+=B[i-1];
        LL ans=0;
        for (int i=1;i<=n;i++)
            ans+=1LL*A[i]*B[i+1];
        printf("%lld\n",ans);
    }
    return 0;
}

  

  

posted @   zzd233  阅读(428)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现

点击右上角即可分享
微信分享提示