题面

Description

给定一个字符串 ss 。现在问你有多少个本质不同的 ss 的子串 t=t1t2⋯tm(m>0)t=t1t2⋯tm(m>0) 使得将 tt 循环左移一位后变成的 t′=t2⋯tmt1t′=t2⋯tmt1 也是 ss 的一个子串。

Input

输入仅有一行,一个字符串 s(1≤lens≤300000)s(1≤lens≤300000) 。字符串 ss 仅包含小写字母。

Output

输出一个整数表示答案。

Sample Input

(样例输入1)
abaac
(样例输入2)
aaa

Sample Output

(样例输出1)
7
(样例输出2)
3

HINT

(样例解释)

第一组数据:符合条件的字符串 tt 有: a, b, c, aa, ab, ba, aba

第二组数据:符合条件的字符串 tt 有: a, aa, aaa

(数据范围与约定)

子任务1(10分): 1≤lens≤2001≤lens≤200

子任务2(30分): 1≤lens≤50001≤lens≤5000

子任务3(60分): 1≤lens≤300000

Solution

这题不算特别难, 但确实是一道好题.

一般而言, 后缀自动机的题目只需要用到后缀树上的连边, 但这题既用了后缀树的边, 又用了后缀自动机上的边.

题目的本质是要我们求出有多少组这样的\((s, c)\), 其中\(s\)为字符串, \(c\)为字符, 使得\(sc\)和\(cs\)都是原串的子串.

考虑枚举原串的每个子串\(s\), 再枚举每个字符\(c\), 则我们只需要判定\(cs\)和\(sc\)是否都是原串的子串即可.

考虑如何枚举原串的每个子串\(s\), 不难想到用后缀树; \(cs\)可以通过后缀树上记录每个节点中包含的每个字符数量以及是否有\(c\)这个儿子来统计; \(sc\)则只需要判断一个节点在后缀自动机上是否有\(c\)这个后继即可.

口胡了这么多, 总之, 就是用后缀树上的边来找前缀, 后缀自动机上的边来找后缀.

#include <cstdio>
#include <cstring> typedef long long LL;
const int N = 5000, K = 47, MOD = (int)1e9 + 7;
int pw[N + 7], pwInv[N + 7];
int a[N + 7], f[N + 7][N + 7], sum[N + 7][N + 7], hsh[N + 7];
inline int getInverse(int a)
{
int res = 1;
for (int x = MOD - 2; x; x >>= 1, a = (LL)a * a % MOD) if (x & 1) res = (LL)res * a % MOD;
return res;
}
inline int getHash(int L, int R)
{
return (LL)(hsh[R] - hsh[L - 1] + MOD) * pwInv[L] % MOD;
}
int main()
{ #ifndef ONLINE_JUDGE freopen("sequence.in", "r", stdin);
freopen("sequence.out", "w", stdout); #endif int n; scanf("%d\n", &n);
pw[0] = 1; for (int i = 1; i <= n; ++ i) pw[i] = (LL)pw[i - 1] * K % MOD, pwInv[i] = getInverse(pw[i]);
hsh[0] = 0;
for (int i = 1; i <= n; ++ i) a[i] = getchar() - '0', hsh[i] = (hsh[i - 1] + (LL)a[i] * pw[i] % MOD) % MOD;
memset(f, 0, sizeof f); memset(sum, 0, sizeof sum);
f[0][0] = 1; for (int i = 0; i <= n; ++ i) sum[0][i] = 1;
for (int i = 1; i <= n; ++ i)
{
for (int j = 1; j <= i; ++ j)
{
if (a[i - j + 1] == 0) continue;
f[i][j] = sum[i - j][j - 1];
if (j <= i - j && getHash(i - j - j + 1, i - j) != getHash(i - j + 1, i))
{
int L = 1, R = j, p;
while (L <= R)
{
int mid = L + R >> 1;
if (getHash(i - j - j + 1, i - j - j + mid) != getHash(i - j + 1, i - j + mid)) p = mid, R = mid - 1;
else L = mid + 1;
}
if (a[i - j + p] > a[i - j - j + p]) f[i][j] = (f[i][j] + f[i - j][j]) % MOD;
}
}
sum[i][0] = 0;
for (int j = 1; j <= n; ++ j) sum[i][j] = (sum[i][j - 1] + f[i][j]) % MOD;
}
printf("%d\n", sum[n][n]);
}

noip2017集训测试赛(十一)Problem C: 循环移位的更多相关文章

  1. noip2017集训测试赛(三) Problem B: mex [补档]

    Description 给你一个无限长的数组,初始的时候都为0,有3种操作: 操作1是把给定区间[l,r][l,r] 设为1, 操作2是把给定区间[l,r][l,r] 设为0, 操作3把给定区间[l, ...

  2. noip2017集训测试赛(六)Problem A: 炮艇大赛之正式赛

    题目描述 给定一个长度为\(L \le 10^9\)的环形赛道, \(n \le 10^5\)个人在上面赛艇. 每个人的速度都不相同, 假如为正则顺时针走, 否则逆时针走. 当两个人相遇时, 他们就会 ...

  3. noip2017集训测试赛(四)Problem A: fibonacci

    题目大意 给你一个序列\(a_1, a_2, ..., a_n\). 我们令函数\(f(n)\)表示斐波那契数列第\(n\)项的值. 总共\(m\)个操作, 分为以下两种: 将\(x \in [L, ...

  4. noip2017集训测试赛(三)Problem C: MST

    题面 Description 给定一个n个点m条边的连通图,保证没有自环和重边.对于每条边求出,在其他边权值不变的情况下,它能取的最大权值,使得这条边在连通图的所有最小生成树上.假如最大权值为无限大, ...

  5. noip2019集训测试赛(二十一)Problem B: 红蓝树

    noip2019集训测试赛(二十一)Problem B: 红蓝树 Description 有一棵N个点,顶点标号为1到N的树.N−1条边中的第i条边连接顶点ai和bi.每条边在初始时被染成蓝色.高桥君 ...

  6. noip2019集训测试赛(二十一)Problem A: Colorful Balls

    Problem A: Colorful Balls Description Snuke放了N个一排彩色的球.从左起第i个球的颜色是ci重量是wi她可以通过执行两种操作对这些球重新排序操作1:选择两个相 ...

  7. 2016集训测试赛(二十一)Problem C: 虫子

    题目大意 给你一棵树, 每个点有一个点权. 有两种操作: link / cut 修改某个点的点权 每次操作后, 你要输出以下答案: 在整棵树中任意选两个点, 这两个点的LCA的期望权值. Soluti ...

  8. 2016北京集训测试赛(十一)Problem C: 树链问题

    Solution 智障暴力题, 每个点维护一下子树信息, 树剖就好了. 我居然还傻了写了一发毛毛虫... #include <cstdio> #include <cctype> ...

  9. 2016集训测试赛(二十六)Problem A: bar

    Solution 首先审清题意, 这里要求的是子串而不是子序列... 我们考虑用1表示p, -1表示j. 用sum[i]表示字符串前\(i\)的前缀和. 则我们考虑一个字符串\([L, R]\)有什么 ...

随机推荐

  1. hadoop +streaming 排序总结

    参考http://blog.csdn.net/baidu_zhongce/article/details/49210787 hadoop用于对key的排序和分桶的设置选项比较多,在公司中主要以KeyF ...

  2. UVALive 6324 Archery (求射箭覆盖的期望)

    #include<cstdio> #include<cmath> #include<cstring> #include<cstdlib> const d ...

  3. c#用UpdatePanel实现接局部刷新

    通常我们看到局部刷新就会想到Ajax,但是我今天要说的是c#的一个控件,只要把服务器按钮和要刷新的区域放在该控件内就能实现局部刷新. 当然它必须和ScriptManager控件一起使用. Update ...

  4. 如何将查询到的数据显示在DataGridView中

    背景介绍: 数据库中的T_Line_Info表中存放着学生上机的记录,也就是我们需要查询上机记录的表,其中具体内容为: 界面设计如下: 右击DataGridView控件,选择编辑列,设计它的列名. 代 ...

  5. 【GXZ的原创】平衡树性能测试

    本文作者为 GXZlegend ,转载请注明 出处 ,谢谢! 〇.序言 前些日子闲的蛋疼做了个平衡树性能测试... 主要是因为学会的平衡树越来越多,做题时却不知道写哪个... 本想结合效率和代码复杂度 ...

  6. CSU 2130~湖南多校对抗第八场 C

    2130: Permutations Submit Page   Summary   Time Limit: 1 Sec     Memory Limit: 128 Mb     Submitted: ...

  7. hihocoder 后缀自动机五·重复旋律8 求循环同构串出现的次数

    描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成的数列. 小Hi发现旋律可以循环,每次把一段旋律里面最前面一个音换到最后面就成为了原旋律的“循环相似旋律”,还可以 ...

  8. select函数详解

    网络编程中一个很重要的函数,没有整理,直接转过来,讲的还是蛮详细的. 转自:http://blog.csdn.net/zhw888888/archive/2009/03/29/4034515.aspx ...

  9. 服务器 阿里云服务器Ubuntu挂载数据盘

    服务器 阿里云服务器Ubuntu挂载数据盘  转自:http://www.codingyun.com/article/24.html coding云运行在阿里云的Ubuntu 12.04 64位操作系 ...

  10. 【CF1016F】Road Projects(贪心)

    题意:给你一棵n 个节点的树,定义1到n的代价是1到 n节点间的最短路径的长度. 现在给你 m 组询问,让你添加一条边权为 w 的边(不与原图重复),求代价的最大值.询问之间相互独立. 1≤n,m≤3 ...