Codeforces 827E Rusty String - 快速傅里叶变换 - 暴力
Grigory loves strings. Recently he found a metal strip on a loft. The strip had length n and consisted of letters "V" and "K". Unfortunately, rust has eaten some of the letters so that it's now impossible to understand which letter was written.
Grigory couldn't understand for a long time what these letters remind him of, so he became interested in the following question: if we put a letter "V" or "K" on each unreadable position, which values can the period of the resulting string be equal to?
A period of a string is such an integer d from 1 to the length of the string that if we put the string shifted by d positions to the right on itself, then all overlapping letters coincide. For example, 3 and 5 are periods of "VKKVK".
There are several (at least one) test cases in the input. The first line contains single integer — the number of test cases.
There is an empty line before each test case. Each test case is described in two lines: the first line contains single integer n(1 ≤ n ≤ 5·105) — the length of the string, the second line contains the string of length n, consisting of letters "V", "K" and characters "?". The latter means the letter on its position is unreadable.
It is guaranteed that the sum of lengths among all test cases doesn't exceed 5·105.
For hacks you can only use tests with one test case.
For each test case print two lines. In the first line print the number of possible periods after we replace each unreadable letter with "V" or "K". In the next line print all these values in increasing order.
3
5
V??VK
6
??????
4
?VK?
2
3 5
6
1 2 3 4 5 6
3
2 3 4
In the first test case from example we can obtain, for example, "VKKVK", which has periods 3 and 5.
In the second test case we can obtain "VVVVVV" which has all periods from 1 to 6.
In the third test case string "KVKV" has periods 2 and 4, and string "KVKK" has periods 3 and 4.
题目大意 给定一个只包含通配符'?'和'v','K'的串,询问所有可能的循环节长度。
首先一个事实就是如果x是可能的循环节,那么2x,3x也一定是。(证明是显然的)
因此可以根据这个愉快的事实进行暴力(似乎出题人在题解的Comments中表示对数据出水了感到歉意)
思路大概就是暴力check如果可行就把它的倍数都标为可行的。
Code
/**
* Codeforces
* Problem#827E
* Accepted
* Time: 265ms
* Memory: 988k
*/
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; const int lim = 5e5; int n;
char str[lim + ];
boolean app[];
boolean able[lim + ]; inline void init() {
scanf("%d", &n);
gets(str);
gets(str);
} inline boolean check(int len) {
for(int i = ; i < len; i++) {
char should = str[i];
for(int j = i + len; j < n; j += len) {
if(should != '?' && str[j] != '?' && should != str[j]) return false;
if(str[j] != '?') should = str[j];
}
}
return true;
} inline void solve() {
app[] = app[] = false;
for(int i = ; i < n; i++) {
switch(str[i]) {
case 'V':
app[] = true;
break;
case 'K':
app[] = true;
break;
}
}
if(!app[] && !app[]) {
printf("%d\n", n);
for(int i = ; i <= n; i++)
printf("%d%c", i, (i == n) ? ('\n') : (' '));
return;
}
int res = ; //, cnt = 0;
for(int i = ; i <= n; i++)
if(!able[i] && check(i))// && (++cnt))
for(int j = i; j <= n; j += i)
res += !able[j], able[j] = true;
printf("%d\n", res);
for(int i = ; i <= n; i++)
if(able[i]) {
printf("%d ", i);
able[i] = false;
}
putchar('\n');
// fprintf(stderr, "%dms counted %d times\n", clock(), cnt);
} int T;
int main() {
scanf("%d", &T);
while(T--) {
init();
solve();
}
return ;
}
Rusty String(Brute force)
然后假设没有这个通配符应该怎么用bitset, 多项式乘法之类的做(因为每个位置除了通配符,只有V或K,而且因为有通配符的存在,所以KMP就不能抓过来用了)
首先根据KMP的思想,如果存在长度为k的循环节那么存在长度为(n - k)的公共前后缀。
所以我们可以把这个串右移k位然后check,最后判一下特殊情况。
为了更快地进行check,所以,我们设A数组中A[I]为1当且仅当s[I] == 'v',B[i]为1当且仅当s[i] == 'K'。
初步可行的条件是
并且
然后为了能够顺利地进行下一步,我们设A'[i] = A[n - i - 1]。于是你会发现两边A'的下标和B的和是一个定值,而且范围不相交。因此我们可以把A'数组和B数组当成两个多项式的系数数组,然后进行FFT。
最开始说的特殊情况是指类似于存在某一个i使得s[i] != s[i + 2k]并且s[i + k] == '?'。
首先可以初步地将一些循环节判断为不可行,对于看似没有问题的循环节长度,我们还需要check它的倍数中有没有被标记为不可行的,如果存在它就不可行(这样做的话就可以把以上的特殊情况处理掉)。
因为数据没有卡暴力,我深深地感受到什么是暴力碾压正解。
Code
/**
* Codeforces
* Problem#827
* Accepted
* Time: 311ms
* Memory: 68200k
*/
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; template<typename T>
class Complex {
public:
T r;
T v; Complex(T r = , T v = ):r(r), v(v) { } Complex operator + (Complex b) {
return Complex(r + b.r, v + b.v);
} Complex operator - (Complex b) {
return Complex(r - b.r, v - b.v);
} Complex operator * (Complex b) {
return Complex(r * b.r - v * b.v, r * b.v + v * b.r);
} Complex operator / (double x) {
return Complex(r / x, v / x);
}
}; const int N = << ;
const double pi = acos(-);
const double eps = 0.5; inline void Rader(Complex<double> *f, int len) {
for(int i = , j = len >> , k; i < len - ; i++) {
if(i < j)
swap(f[i], f[j]);
for(k = len >> ; j >= k; j -= k, k >>= );
if(j < k)
j += k;
}
} inline void fft(Complex<double> *f, int len, int sign) {
Rader(f, len);
for(int l = ; l <= len; l <<= ) {
Complex<double> wn(cos( * pi / l), sin( * pi * sign / l)), u, v;
int hl = l >> ;
for(int i = ; i < len; i += l) {
Complex<double> w(, );
for(int j = ; j < hl; j++, w = w * wn) {
u = f[i + j], v = w * f[i + j + hl];
f[i + j] = u + v, f[i + j + hl] = u - v;
}
}
}
if(sign == -)
for(int i = ; i < len; i++)
f[i] = f[i] / len;
} int n, len;
char str[];
Complex<double> A[N], B[N]; inline void init() {
scanf("%d", &n);
gets(str);
gets(str);
for(len = ; len < (n << ); len <<= );
memset(A, , sizeof(Complex<double>) * (len + ));
memset(B, , sizeof(Complex<double>) * (len + ));
for(int i = ; i < n; i++) {
if(str[i] == 'V')
A[n - i - ].r = ;
else if(str[i] == 'K')
B[i].r = ;
}
} boolean bad[N];
int res = ;
inline void solve() {
fft(A, len, );
fft(B, len, );
for(int i = ; i < len; i++) A[i] = A[i] * B[i];
fft(A, len, -);
memset(bad, false, sizeof(boolean) * (n + ));
// for(int i = 1; i < n; i++)
// if(A[n - i - 1].r >= eps || A[n + i - 1].r >= eps)
// bad[i] = true;
for(int i = ; i < len; i++)
if(A[i].r >= eps)
bad[abs(i - n + )] = true;
for(int i = ; i < n; i++)
if(!bad[i])
for(int j = i << ; j < n; j += i)
if(bad[j]) {
bad[i] = true;
break;
}
int res = ;
for(int i = ; i <= n; i++)
if(!bad[i])
res++;
printf("%d\n", res);
for(int i = ; i <= n; i++) {
if(!bad[i])
printf("%d ", i);
}
putchar('\n');
} int T;
int main() {
scanf("%d", &T);
while(T--) {
init();
solve();
}
return ;
}
Codeforces 827E Rusty String - 快速傅里叶变换 - 暴力的更多相关文章
- CF 827E Rusty String FFT
传送门 如果没有碍事的?的话,判定字符串的循环节直接用KMP的失配数组就可以搞定.现在有了碍事的?,我们就需要考虑更通用的算法. 考虑KMP失配数组判定字符串循环节的本质,发现判定\(k\)是否为字符 ...
- 多项式 之 快速傅里叶变换(FFT)/数论变换(NTT)/常用套路【入门】
原文链接https://www.cnblogs.com/zhouzhendong/p/Fast-Fourier-Transform.html 多项式 之 快速傅里叶变换(FFT)/数论变换(NTT)/ ...
- 【知识总结】快速傅里叶变换(FFT)
这可能是我第五次学FFT了--菜哭qwq 先给出一些个人认为非常优秀的参考资料: 一小时学会快速傅里叶变换(Fast Fourier Transform) - 知乎 小学生都能看懂的FFT!!! - ...
- 再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Bluestein算法+分治FFT+FFT的优化+任意模数NTT)
再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Bluestein算法+分治FFT+FFT的优化+任意模数NTT) 目录 再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Blueste ...
- 快速傅里叶变换FFT& 数论变换NTT
相关知识 时间域上的函数f(t)经过傅里叶变换(Fourier Transform)变成频率域上的F(w),也就是用一些不同频率正弦曲线的加 权叠加得到时间域上的信号. \[ F(\omega)=\m ...
- 快速傅里叶变换(FFT)
扯 去北京学习的时候才系统的学习了一下卷积,当时整理了这个笔记的大部分.后来就一直放着忘了写完.直到今天都腊月二十八了,才想起来还有个FFT的笔记没整完呢.整理完这个我就假装今年的任务全都over了吧 ...
- 51Nod 快速傅里叶变换题集选刷
打开51Nod全部问题页面,在右边题目分类中找到快速傅里叶变换,然后按分值排序,就是本文的题目顺序. 1.大数乘法问题 这个……板子就算了吧. 2.美妙的序列问题 长度为n的排列,且满足从中间任意位置 ...
- 快速傅里叶变换FFT / NTT
目录 FFT 系数表示法 点值表示法 复数 DFT(离散傅里叶变换) 单位根的性质 FFT(快速傅里叶变换) IFFT(快速傅里叶逆变换) NTT 阶 原根 扩展知识 FFT 参考blog: 十分简明 ...
- 快速傅里叶变换(FFT)相关内容汇总
(原稿:https://paste.ubuntu.com/p/yJNsn3xPt8/) 快速傅里叶变换,是求两个多项式卷积的算法,其时间复杂度为$O(n\log n)$,优于普通卷积求法,且根据有关证 ...
随机推荐
- python对字典及列表递归排序
对字典内所有内容进行排升序排序,包括,子数组,子字典 需要注意: 1.字典因为是在一定程序上无序的,所以这里采用了内置包,来变成有序字典 from collections import Ordered ...
- VirtualBox 报错VERR_VD_IMAGE_READ_ONLY
VirtualBox 无法打开虚拟机,报错VERR_VD_IMAGE_READ_ONLY,详细报错如下: 不能为虚拟电脑 Primary11gRAC2 打开一个新任务. Failed to open ...
- 47.HTML---frame,iframe,frameset之间的关系与区别
iframe 是在html页面内嵌入框架 框架内可以连接另一个页面, 如 <html> <head></head> <body> <iframe ...
- java微信小程序调用支付接口(转)
简介:微信小程序支付这里的坑还是有的,所以提醒各位在编写的一定要注意!!! 1.首先呢,你需要准备openid,appid,还有申请微信支付后要设置一个32位的密钥,需要先生成一个sign,得到pre ...
- ubuntu安装rvm
sudo apt-get install curl git-core bash -s stable < <(curl -s https://raw.github.com/wayneeseg ...
- 改装原生的dialog
改装 dialog 定制 View rootView = LayoutInflater.from(mContext). inflate(R.layout.nfc_tag_name_dialog, nu ...
- html5-移动端布局模板
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8&qu ...
- sitecore系统教程之内容创作入门
在Sitecore中,有两种编辑工具,您可以在其中创建和编辑网站上的内容: 内容编辑器 - 专为熟悉Sitecore及其包含的功能的经验丰富的内容作者而设计的应用程序. 体验编辑器 - 一种直观的编辑 ...
- JavaScript <script>标签的位置、延迟脚本(defer属性)与 异步脚本(async属性)
一.<script>标签的位置 传统的做法是将<script>元素放在<head>元素中,例如: <!DOCTYPE html><html > ...
- android手机平板如何使用usb有线网卡
最近有个项目需要在android平板上使用usb有线网卡,所以做了一部分工作,在这里简单总结一下. 我在TB上购买了一个micro-usb接口的android免驱有线网卡,这个网上很多,随便买一个符合 ...