【题解】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. 面向对象的封装、继承和多态特性_python

    一.面向对象的几个特点 面向对象也称为类,拥有下面几个特点 1.封装特性:利用类的__init__(self)构造方法封装对象 构造方法:__init__(self):在生成对象的时候会自动调用 例子 ...

  2. jQuery 抖动特效函数封装

    <style> ul{ margin-top: 100px; } li { float: left; margin-left: 20px; position: absolute; top: ...

  3. Redis09——事务(悲观锁、乐观锁)

    事务 定义: Redis事务是一个单独的隔离操作 ①事务中所有的命令都会被序列化.按照顺序执行 ②事务在执行过程中不会被其他客户端发送来的命令请求打断 作用: 串联多个命令防止别的命令插队 multi ...

  4. (转)json格式转换成javaBean对象的方法

    把json格式转换成javaBean才可以.于是查了一下资料,网上最多的资料就是下面的这种方式: Java code? 1 2 3 4 5 6 7 8 9 String str = "[{\ ...

  5. [蓝桥杯][基础训练]Sine之舞

    Description 最近FJ为它的奶牛开设了数学分析课,FJ知道,若要学好这门课,必须有一个好的三角函数基本功. 所以他为奶牛们做了一个“Sine之舞”的游戏,寓教于乐,提高奶牛的计算能力. 不妨 ...

  6. php:对象(object)数据类型实例详解

    什么是对象? 对象是存储数据和有关如何处理数据的信息的数据类型.是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位.一个对象由一组属性和对这组属性进行操作的一组服务组成. 语法 在 PHP ...

  7. YARN High Availablity

    1. RM Failover ResourceManager HA 由一个Active/Standby 架构实现:在任何时间点,仅有一个RM是Active,其他一个(或多个)RM节点处于Standby ...

  8. ubuntu 终端快捷方式汇总

    terminal 是一个命令行终端,将启动系统默认的shell,shell是一个解释并执行在命令行提示符输入的命令的程序. 启动 terminal1 在 “面板主页” 的应用程序搜索栏中,输入命令gn ...

  9. django 0

    web 框架的本质 - socket服务端 socket 收发消息 根据不同的路径返回不同的内容 模板渲染 jinja2 字符串替换 mvc model(模型/db) view(展示html) con ...

  10. HTMLElement.offsetParent(offsetLeft和offsetTop参照offsetParent的内边距边界)

    IE7以上(不是火狐): 父级没有定位: 本身没有定位: ==> offsetParent:body 本身定位为:absolute/relative: ==> offsetParent:b ...