【题解】Rusty String [CF827E]

传送门:\(\text{Rusty String}\) \(\text{[CF827E]}\)

【题目描述】

多组数据,每组数据给出一个由 \(V,K,?\) 三种字符组成的字符串,其中 \(?\) 可以替换为 \(V,K\) 中的任意一个,求替换后的字符串可以存在的循环周期有哪些。

【分析】

乍一看像是带通配符的字符串匹配(不会的强烈建议去康康这个),由于字符集不大,按照套路就应该枚举两个字符 \(a \ne b\in\{V,K\}\),然后设 \(f(i)=[A[i]=a],\) \(g(i)=[A[i]=b]\)

则有: \(PA(k)=\sum_{i=1}^{n}f(i)g(i+k)\),翻转 \(f\) 得到:\(PA(k)=\sum_{i=1}^{n}f(n-i+1)g(i+k)\) \(=\sum_{i+j=n+k+1}f(i)g(j)\)

很明显是个 \(\text{FFT}\) 的板子,把式子卷起来就可以了。

按照以往的“经验”,或者说惯性思维,多半会这样做(比如说我),但写出来却发现酱紫连第一个样例都过不了。

那么问题出在哪儿了呢?

注意:当 \(PA(k) \ne 0\) 时说明在某个地方出现了 \('V'\) 与 \('K'\) 相对应的情况,以 \(k\) 作为周期肯定不合法,但 \(PA(k) = 0\) 并不能说明 \(k\) 一定是合法周期,因为这道题中的特殊符号不是通配符,而是未知字符,通配符可以任意匹配,但未知字符只能匹配一种。

比如样例 \(1\),用上面的错误算法多输出了一个 \(2\),在这种情况下 \(S_3('?')\) 分别与 \(S_1('V'),S_5('K')\) 进行了匹配,因此以 \(2\) 作为周期是不合法的。

考虑改进算法:

之前的做法错在算出来的式子只进行了一周目的匹配判断,那么我们只要将二周目、三周目……全部都判断一下就好了呀,正巧序列 \(PA\) 完美的包含了所有长度的循环情况,所以直接暴力枚举周期的倍数即可(复杂度为 \(O(\frac{n}{1}+\frac{n}{2}+\frac{n}{3} \cdots \frac{n}{n})=O(nlogn)\))。

总时间复杂度为:\(O(nlogn)\) 。

你说啥?卡常?玄学优化减少调用次数?不存在的,我写了这么久的 \(\text{NTT}\),还从未被卡过(小骄傲)。

\(\text{emm...}\)

等等,还没完,仔细看题啊:“保证输入字符串总长不超过 \(5∗10^5\) 。” 注意是输入总长,所以不要偷懒用 \(\text{memset}\)!

【Code】

#include<algorithm>
#include<cstring>
#include<cstdio>
#define LL long long
#define Re register int
using namespace std;
const int N=1048576+3,P=998244353,G=3;
int n,T,ans,invG,f[N],g[N],tr[N],PA[N],can[N];char A[N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline int mi(Re x,Re k){
Re s=1;
while(k){
if(k&1)s=(LL)s*x%P;
x=(LL)x*x%P,k>>=1;
}
return s;
}
inline int inv(Re x){return mi(x,P-2);}
inline void NTT(Re *f,Re n,Re op){
for(Re i=0;i<n;++i)if(i<tr[i])swap(f[i],f[tr[i]]);
for(Re p=2;p<=n;p<<=1){
Re len=p>>1,w1=mi(op?invG:G,(P-1)/p);
for(Re st=0;st<n;st+=p)
for(Re j=st,base=1;j<=st+len-1;++j){
Re tmp=(LL)base*f[j+len]%P;
f[j+len]=(f[j]-tmp+P)%P,(f[j]+=tmp)%=P;
base=(LL)base*w1%P;
}
}
}
inline void sakura(Re *f,Re n,Re *g,Re m){//卷卷卷
Re n_=n;
for(m+=n,n=1;n<=m;n<<=1);Re invn=inv(n);
for(Re i=n_+1;i<=n;++i)f[i]=g[i]=0;//注意初始化
for(Re i=1;i<n;++i)tr[i]=(tr[i>>1]>>1)|((i&1)?n>>1:0);
NTT(f,n,0),NTT(g,n,0);
for(Re i=0;i<n;++i)f[i]=(LL)f[i]*g[i]%P;
NTT(f,n,1);
for(Re i=0;i<=m;++i)f[i]=(LL)f[i]*invn%P;
}
int main(){
// freopen("123.txt","r",stdin);
in(T),invG=inv(G);
while(T--){
in(n),scanf("%s",A+1),ans=0;
for(Re i=1;i<=n;++i)PA[i]=0;//注意初始化
for(Re i=1;i<=n;++i)f[n-i+1]=(A[i]=='K'),g[i]=(A[i]=='V');
sakura(f,n,g,n);
for(Re i=1;i<n;++i)PA[i]+=f[i+n+1];//PA[n]可以不用管,因为一定合法
for(Re i=1;i<=n;++i)f[n-i+1]=(A[i]=='V'),g[i]=(A[i]=='K');
sakura(f,n,g,n);
for(Re i=1;i<n;++i)PA[i]+=f[i+n+1];
for(Re i=1;i<=n;++i){
can[i]=1;
for(Re j=i;j<=n&&can[i];j+=i)can[i]&=(!PA[j]);//枚举倍数依次判断
ans+=can[i];
}
printf("%d\n",ans);
for(Re i=1;i<=n;++i)if(can[i])printf("%d ",i);puts("");
}
}

【题解】Rusty String [CF827E]的更多相关文章

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

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

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

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

  3. E. Rusty String

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

  4. PAT甲题题解-1050. String Subtraction (20)-水题

    #include <iostream> #include <cstdio> #include <string.h> #include <algorithm&g ...

  5. CF 827E Rusty String FFT

    传送门 如果没有碍事的?的话,判定字符串的循环节直接用KMP的失配数组就可以搞定.现在有了碍事的?,我们就需要考虑更通用的算法. 考虑KMP失配数组判定字符串循环节的本质,发现判定\(k\)是否为字符 ...

  6. 《LeetBook》leetcode题解(8): String to Integer (atoi) [E]——正负号处理

    我现在在做一个叫<leetbook>的免费开源书项目,力求提供最易懂的中文思路,目前把解题思路都同步更新到gitbook上了,需要的同学可以去看看 书的地址:https://hk029.g ...

  7. LeetCode题解 #8 String to Integer (atoi)

    又是一道恶心的简单题. 一开始没想到这么多情况的,幸好LeetCode是个很人性化的oj,能让你知道你在哪个case上错了,否则一辈子都过不了. 考虑不周到只能一个个补了. 列举一下恶心的case / ...

  8. leetcode个人题解——#8 string to integer

    第八题 class Solution { public: int myAtoi(string str) { ; ; ; while(str[i] == ' ')i++; if (str[i] == ' ...

  9. 题解-Codeforces710F String Set Queries

    咕了好久没更博客,最近得知可以去冬眠营玩耍,还可以搭顺风车回广州过年 (最近做到的比较有意思的题目:bzoj3958.hihocoder1419) Problem Codeforces-710F--洛 ...

随机推荐

  1. java异常处理课后作

    1.动手动脑 源码 import javax.swing.*; class AboutException {   public static void main(String[] a)    {    ...

  2. git 解决每次更新代码都要输入用户名密码

    git config --global credential.helper store git pull /git push (第一次输入,后续就不用再次数据)

  3. SDF数据库

    一.SDF数据库初探 SDF是一个标准缩略数据库格式.这个数据库包含扩展名为.sdf的文件并且以结构化文件格式进行数据存储.这些SDF文件通常用于在不同数据库应用之间移动数据.它允许一个用户将一个软件 ...

  4. JQ DOM元素 创建 添加 删除

    创建元素 // 创建元素节点 $('<p></p>'); // 创建属性节点 $('<p class="wow"></p>'); / ...

  5. Centos7 安装 GitLab 代码管理服务器

    一.安装依赖 yum -y install policycoreutils openssh-server openssh-clients postfix 二.启动postfix,并设置开机自启动 sy ...

  6. 不能暴露服务给外部环境,因为nginx-ingress-control启动失败

    不能暴露服务给外部环境,因为nginx-ingress-control启动失败 待办 rancher 和k8s中的端口冲突,nginx-ingress-control都需要使用80端口 以及443端口 ...

  7. MySQL学习(七) 索引选择(半原创)

    概述 该篇文章主要阐述一个例子(例子来自参考资料,侵删),然后总结今天相关的知识点. 例子 (例子来自参考文章,非原创) 创建表并插入数据,并执行查询 CREATE TABLE `t` ( `id` ...

  8. 基于原生PHP的路由分配实现

    对于由原生PHP写成的独立PHP框架,利用单一入口文件实现路径的访问.这时我们会遇到的首要问题是:文件的相互包含,其次就是路由分配.当我们不利用成熟的PHP框架进行web开发时,我们就会发现上述两个问 ...

  9. CefApp和CefClient的作用

    CefApp 在cefsimple中,提到了一个cefapp的类型,这个类型是一个接口类,主要目的是提供获取三种handler的接口 /// // Implement this interface t ...

  10. Centos7 下mysql 密码重置

    Centos7 下mysql 密码重置 先停止mysql服务 mysqld_safe --skip-grant-tables & mysql mysql> use mysql;mysql ...