传送门

如果没有碍事的?的话,判定字符串的循环节直接用KMP的失配数组就可以搞定。现在有了碍事的?,我们就需要考虑更通用的算法。

考虑KMP失配数组判定字符串循环节的本质,发现判定\(k\)是否为字符串的循环节等价于判定字符串在右移\(k\)位后能否和原字符串匹配(只考虑二者重叠的部分)。

我们不妨先把?直接看成一个可以匹配任何字符的通配符,而解决带通配符的字符串匹配问题的一个算法就是FFT

(以下默认下标为\(0\)~\(n-1\))

设字符串为\(s\),因为字符集只有\(2\),不妨直接枚举不能匹配的两种情况。定义\(a_i=[s_i='V']\),\(b_i=[s_i='K']\),另一种情况只需要把定义反过来就行了。

再定义一个数组

\[\begin{align}c_i=\sum_{j=0}^{n-1}a_{i+j}b_j\end{align}
\]

(另一种情况式子是一样的)

发现\(s\)右移\(i\)位后能和原串匹配当且仅当两种情况的\(c_i\)都为\(0\)。

而上面的式子把\(\{a_i\}\)反转后就是

\[\begin{align}c_i=\sum_{j=0}^{n-1}a^R_{n-i-j-1}b_{j}\end{align}=\left(a^R*b\right)_{n-i-1}
\]

跑两遍卷积即可。

然而还是会发现一些问题……从样例就能看出来,这里的?其实并不是通配符,因为在判定循环节时一个?在不同的位置必须代表相同的字符(可能有点抽象,参见第一组样例中2为什么可以匹配但不是循环节)。

然而这个问题其实并不棘手。我们都知道如果一个数真的是循环节那么它的所有倍数也一定是循环节,所以对于那些\(c_i\)为\(00\)的位置再判断一下它的倍数是否也都满足\(c_i=0\)就行了。

(完整证明参见官方题解)

这一步的复杂度是\(O(n\log n)\)的,不会影响到总复杂度。

另外,考虑到问题的特殊性,其实不必跑两遍卷积,任意跑其中一种情况即可,两种情况的\(c_i\)分别是\(\left(a^R*b\right)_{n-i-1}\)和\(+\left(a^R*b\right)_{n+i-1}\)(可以从两种情况的对称性的角度理解)。

(我比较懒写的是NTT)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1048600,p=998244353,g=3;
void NTT(int*,int,int);
int qpow(int,int);
char s[maxn];
bool ans[maxn];
int T,n,A[maxn],B[maxn];
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%s",&n,s);
int N=1;
while(N<(n<<1))N<<=1;
memset(A,0,sizeof(int)*N);
memset(B,0,sizeof(int)*N);
for(int i=0;i<n;i++){
if(s[i]=='V')A[n-i-1]=1;
else if(s[i]=='K')B[i]=1;
}
NTT(A,N,1);
NTT(B,N,1);
for(int i=0;i<N;i++)A[i]=(long long)A[i]*B[i]%p;
NTT(A,N,-1);
int cnt=0;
for(int i=1;i<=n;i++){
ans[i]=true;
for(int j=i;j<=n;j+=i)ans[i]&=!(A[n-j-1]+A[n+j-1]);
cnt+=ans[i];
}
printf("%d\n",cnt);
for(int i=1;i<=n;i++)
if(ans[i]){
printf("%d",i);
if(--cnt)printf(" ");
}
printf("\n");
}
return 0;
}
void NTT(int *A,int n,int tp){
for(int i=1,j=0,k;i<n-1;i++){
k=n;
do j^=(k>>=1);while(j<k);
if(i<j)swap(A[i],A[j]);
}
for(int k=2;k<=n;k<<=1){
int wn=qpow(g,tp>0?(p-1)/k:p-1-(p-1)/k);
for(int i=0;i<n;i+=k){
int w=1;
for(int j=0;j<(k>>1);j++,w=(long long)w*wn%p){
int a=A[i+j],b=(long long)w*A[i+j+(k>>1)]%p;
A[i+j]=a+b;
if(A[i+j]>=p)A[i+j]-=p;
A[i+j+(k>>1)]=a-b;
if(A[i+j+(k>>1)]<0)A[i+j+(k>>1)]+=p;
}
}
}
if(tp<0){
int inv=qpow(n,p-2);
for(int i=0;i<n;i++)A[i]=(long long)A[i]*inv%p;
}
}
int qpow(int a,int b){
int ans=1;
for(;b;b>>=1,a=(long long)a*a%p)if(b&1)ans=(long long)ans*a%p;
return ans;
}

CF 827E Rusty String FFT的更多相关文章

  1. Codeforces 827E Rusty String - 快速傅里叶变换 - 暴力

    Grigory loves strings. Recently he found a metal strip on a loft. The strip had length n and consist ...

  2. 【CF827E】Rusty String 调和级数+FFT

    [CF827E]Rusty String 题意:给你一个01串,其中部分字符是'?',?可以是0或1,求所有可能的d,满足存在一种可能得到的01串,在向右移动d格后与自己相同. $n\le 5\tim ...

  3. E. Rusty String

    E. Rusty String time limit per test 3 seconds memory limit per test 512 megabytes input standard inp ...

  4. 【题解】Rusty String [CF827E]

    [题解]Rusty String [CF827E] 传送门:\(\text{Rusty String}\) \(\text{[CF827E]}\) [题目描述] 多组数据,每组数据给出一个由 \(V, ...

  5. CF 1131 E. String Multiplication

    E. String Multiplication 题意 分析: 从后往前考虑字符串变成什么样子. 设$S_i = p_1 \cdot p_2 \dots p_{i}$,最后一定是$S_{n - 1} ...

  6. CF 1003B Binary String Constructing 【构造/找规律/分类讨论】

    You are given three integers a, b and x. Your task is to construct a binary string s of length n=a+b ...

  7. CF #541 E. String Multiplication

    题意: 给定一系列字符串,每次都是后一个字符串和前面的融合,这个融合操作就是原来的串分成独立的,然后把新串插入到这些空格中.问最后,最长的相同连续的长度. 思路: 这道题可以贪心的来,我们压缩状态,记 ...

  8. CF 1140B Good String

    Description You have a string ss of length nn consisting of only characters > and <. You may d ...

  9. 【CF 710F】String Set Queries

    在校内OJ上A了,没有加强制在线的东西..不放链接了. 这道题题意是维护一个字符串集合,支持三种操作: 1.加字符串 2.删字符串 3.查询集合中的所有字符串在给出的模板串中出现的次数 操作数\(m ...

随机推荐

  1. 电脑网络IP固定地址自动改变!

    今天电脑的固定IP地址每次重启设备,会自动改变一次.所以每次重启电脑都要手动重设IP地址.网关.DNS及高级选项中的ip设置. 高级选项中的ip设置每次都有2个ip,都要我删除一个.我都崩溃了,还以为 ...

  2. 怎么在eclipse中访问webservice

    在eclipse创建webservice的方法: 1.在Eclipse的菜单栏中,Window --> Preferences --> Web Service --> Axis2 P ...

  3. 直接访问实例变量 VS 通过点语法访问实例变量

    直接访问实例变量,不会经过 OC 的方法派发机制,速度比较块.会直接访问对象的实例变量对应的内存. 直接访问实例变量,不会调用"设置方法".绕过了相关属性对应的"内存管理 ...

  4. day 52 Django 的中间件加载顺序

    前情提要: django的中间键的作用是进行加载 可以通过中间键进行辅助操作 1.中间件的概念 中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局 ...

  5. nginx配置跨域、gzip加速、代理详细讲解

    1.配置跨域 这个很简单,直接打开配置nginx.conf ,在http下配置下面三行代码:当然如果你是想某一个虚拟主机下跨域,那就在哪个server下面添加 add_header Access-Co ...

  6. LeetCode题解-147 对链表进行插入排序 Medium

    对链表进行插入排序. 插入排序的动画演示如上.从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示). 每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中. 插 ...

  7. 2、如何解决xamarin没有相关教程的的指导贴

    本篇文章主要在于解决xamarin相关文档偏少的问题. 最终的代码并不重要.重要的还是那种处理的方式 授人以渔 群里有群友讨论说需要读取安卓的 充电电流.这样的问题实际上在原生java有一堆.但是到了 ...

  8. 使用Second Copy同步ftp服务器的差异文件

    公司一直用自主开发的一个同步工具来进行数据库文件异机备份的,但无奈太不稳定,三天两头出现服务挂死的问题,特别是最近这1个月,几天就1次. 问题现象都是服务一直在运行,但没有复制文件到备份机上,而且备份 ...

  9. sersync+rsync=实时异步备份

    环境准备 服务器两台 rsync-server:192.168.1.8  (备份服务器) sersync-node1:192.168.1.9 (需要备份的服务器) 系统 CentOS7.4 关闭防火墙 ...

  10. solr 6.6 基础环境搭建 (一)

    Apache Solr 介绍 参考博主原文链接1:http://www.cnblogs.com/blueskyli/p/7100443.html 参考博主原文链接2:http://www.cnblog ...