这里会显示出您选择的修订版和当前版本之间的差别。
| 两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
|
2020-2021:teams:legal_string:jxm2001:字符串_1 [2020/08/27 10:46] jxm2001 |
2020-2021:teams:legal_string:jxm2001:字符串_1 [2020/08/27 11:38] (当前版本) jxm2001 |
||
|---|---|---|---|
| 行 21: | 行 21: | ||
| 如果 $s_{i+1}\neq s_{f(i)+1}$,则只能考虑第二长的真前后缀,暂时记为 $j$。 | 如果 $s_{i+1}\neq s_{f(i)+1}$,则只能考虑第二长的真前后缀,暂时记为 $j$。 | ||
| - | 于是有 $s[1,j]=s[i-j+1,i]=s[f(i)-j+1,f(i)]$,发现 $j$ 是 $s[1,f(i)]$ 的真前后缀,于是有 $j=f(f(i))$。 | + | 根据已知,有 $s[1,j]=s[i-j+1,i]=s[f(i)-j+1,f(i)]$。 |
| + | |||
| + | 发现 $j$ 是 $s[1,f(i)]$ 的真前后缀,于是有 $j=f(f(i))$。 | ||
| 不停尝试,直到 $j=0$ 即没有满足条件真前后缀,可以停止尝试。 | 不停尝试,直到 $j=0$ 即没有满足条件真前后缀,可以停止尝试。 | ||
| 行 324: | 行 326: | ||
| 考虑求出每个位置的 $z$ 函数,然后找到与前缀相同的在中间出现过的最长字串,有 $\text{pre}=\max_{1\lt i \lt n}(\min(z(i),n-i))$。 | 考虑求出每个位置的 $z$ 函数,然后找到与前缀相同的在中间出现过的最长字串,有 $\text{pre}=\max_{1\lt i \lt n}(\min(z(i),n-i))$。 | ||
| - | 接下来从大到小枚举每个与前缀相同的后缀,判定条件为 $i+z(i)-1=n$,如果该后缀长度小于 $\text{pre}$,则得到答案。时间复杂度 $O(|S|)$。 | + | 接下来从大到小枚举每个与前缀相同的后缀,判定条件为 $i+z(i)-1=n$。如果该后缀长度小于 $\text{pre}$,则得到答案。时间复杂度 $O(|S|)$。 |
| <hidden 查看代码> | <hidden 查看代码> | ||
| 行 356: | 行 358: | ||
| else | else | ||
| _rep(i,1,ans)putchar(s[i]); | _rep(i,1,ans)putchar(s[i]); | ||
| + | return 0; | ||
| + | } | ||
| + | </code> | ||
| + | </hidden> | ||
| + | |||
| + | == 题解 2 == | ||
| + | |||
| + | 考虑求出每个位置的 $f$ 函数,同样找到与前缀相同的在中间出现过的最长字串,有 $\text{pre}=\max_{1\lt i \lt n}f(i)$。 | ||
| + | |||
| + | 接下来从大到小枚举每个与前缀相同的后缀,初始为 $f(n)$,然后不断迭代 $f$ 即可。如果该后缀长度小于 $\text{pre}$,则得到答案。时间复杂度 $O(|S|)$。 | ||
| + | |||
| + | === 习题二 === | ||
| + | |||
| + | [[https://www.luogu.com.cn/problem/CF432D|CF432D]] | ||
| + | |||
| + | == 题意 == | ||
| + | |||
| + | 给你一个长度为 $n$ 的字符串。定义“完美子串”既是它的前缀也是它的后缀,求“完美子串”的个数且统计这些子串的在字符串中出现的次数。 | ||
| + | |||
| + | == 题解 1 == | ||
| + | |||
| + | 考虑求出每个位置的 $z$ 函数,统计每个前缀出现次数。关于 $z$ 函数统计前缀次数,如果 $s[l,r]=s[1,r-l+1]$,显然有 $s[l,r-1]=s[1,r-l]$。 | ||
| + | |||
| + | 于是可以直接用桶维护 $z$ 函数,然后求后缀和。 | ||
| + | |||
| + | 然后从大到小枚举每个与前缀相同的后缀并记录答案,判定条件为 $i+z(i)-1=n$。时间复杂度 $O(|S|)$。 | ||
| + | |||
| + | <hidden 查看代码> | ||
| + | <code cpp> | ||
| + | const int MAXN=1e5+5; | ||
| + | namespace KMP{ | ||
| + | int z[MAXN]; | ||
| + | void get_z(char *s,int n){ | ||
| + | z[1]=n; | ||
| + | _rep(i,2,n)z[i]=0; | ||
| + | for(int i=2,l=0,r=0;i<=n;i++){ | ||
| + | if(i<=r)z[i]=min(z[i-l+1],r-i+1); | ||
| + | while(i+z[i]<=n&&s[z[i]+1]==s[i+z[i]])z[i]++; | ||
| + | if(i+z[i]-1>r)l=i,r=i+z[i]-1; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | int c[MAXN]; | ||
| + | char s[MAXN]; | ||
| + | int main() | ||
| + | { | ||
| + | gets(s+1); | ||
| + | int n=strlen(s+1); | ||
| + | KMP::get_z(s,n); | ||
| + | stack<int> s; | ||
| + | _rep(i,1,n){ | ||
| + | if(i+KMP::z[i]-1==n) | ||
| + | s.push(KMP::z[i]); | ||
| + | c[KMP::z[i]]++; | ||
| + | } | ||
| + | for(int i=n;i;i--)c[i-1]+=c[i]; | ||
| + | enter(s.size()); | ||
| + | while(!s.empty()){ | ||
| + | printf("%d %d\n",s.top(),c[s.top()]); | ||
| + | s.pop(); | ||
| + | } | ||
| + | return 0; | ||
| + | } | ||
| + | </code> | ||
| + | </hidden> | ||
| + | |||
| + | == 题解 2 == | ||
| + | |||
| + | 考虑求出每个位置的 $f$ 函数,统计每个前缀出现次数。 | ||
| + | |||
| + | 然后从大到小枚举每个与前缀相同的后缀并记录答案,初始为 $n$,然后不断迭代 $f$ 即可。时间复杂度 $O(|S|)$。 | ||
| + | |||
| + | <hidden 查看代码> | ||
| + | <code cpp> | ||
| + | const int MAXN=1e5+5; | ||
| + | namespace KMP{ | ||
| + | int f[MAXN]; | ||
| + | void get_f(char *s,int n){ | ||
| + | int pos=0; | ||
| + | f[1]=0; | ||
| + | _rep(i,2,n){ | ||
| + | while(pos&&s[i]!=s[pos+1])pos=f[pos]; | ||
| + | if(s[i]==s[pos+1])pos++; | ||
| + | f[i]=pos; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | char s[MAXN]; | ||
| + | int c[MAXN]; | ||
| + | int main() | ||
| + | { | ||
| + | gets(s+1); | ||
| + | int n=strlen(s+1); | ||
| + | KMP::get_f(s,n); | ||
| + | _rep(i,1,n)c[KMP::f[i]]++; | ||
| + | stack<pair<int,int> >pt; | ||
| + | for(int i=n;i;i--)c[KMP::f[i]]+=c[i]; | ||
| + | for(int i=n;i;i=KMP::f[i]) | ||
| + | pt.push(make_pair(i,c[i]+1)); | ||
| + | enter(pt.size()); | ||
| + | while(!pt.empty()){ | ||
| + | printf("%d %d\n",pt.top().first,pt.top().second); | ||
| + | pt.pop(); | ||
| + | } | ||
| return 0; | return 0; | ||
| } | } | ||
| </code> | </code> | ||
| </hidden> | </hidden> | ||