Codeforces 827E Rusty String - 快速傅里叶变换 - 暴力
题目大意 给定一个只包含通配符'?'和'v','K'的串,询问所有可能的循环节长度。
首先一个事实就是如果x是可能的循环节,那么2x,3x也一定是。(证明是显然的)
因此可以根据这个愉快的事实进行暴力(似乎出题人在题解的Comments中表示对数据出水了感到歉意)
思路大概就是暴力check如果可行就把它的倍数都标为可行的。
Code
1 /** 2 * Codeforces 3 * Problem#827E 4 * Accepted 5 * Time: 265ms 6 * Memory: 988k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 const int lim = 5e5; 13 14 int n; 15 char str[lim + 1]; 16 boolean app[3]; 17 boolean able[lim + 1]; 18 19 inline void init() { 20 scanf("%d", &n); 21 gets(str); 22 gets(str); 23 } 24 25 inline boolean check(int len) { 26 for(int i = 0; i < len; i++) { 27 char should = str[i]; 28 for(int j = i + len; j < n; j += len) { 29 if(should != '?' && str[j] != '?' && should != str[j]) return false; 30 if(str[j] != '?') should = str[j]; 31 } 32 } 33 return true; 34 } 35 36 inline void solve() { 37 app[0] = app[1] = false; 38 for(int i = 0; i < n; i++) { 39 switch(str[i]) { 40 case 'V': 41 app[0] = true; 42 break; 43 case 'K': 44 app[1] = true; 45 break; 46 } 47 } 48 if(!app[0] && !app[1]) { 49 printf("%d\n", n); 50 for(int i = 1; i <= n; i++) 51 printf("%d%c", i, (i == n) ? ('\n') : (' ')); 52 return; 53 } 54 int res = 0; //, cnt = 0; 55 for(int i = 1; i <= n; i++) 56 if(!able[i] && check(i))// && (++cnt)) 57 for(int j = i; j <= n; j += i) 58 res += !able[j], able[j] = true; 59 printf("%d\n", res); 60 for(int i = 1; i <= n; i++) 61 if(able[i]) { 62 printf("%d ", i); 63 able[i] = false; 64 } 65 putchar('\n'); 66 // fprintf(stderr, "%dms counted %d times\n", clock(), cnt); 67 } 68 69 int T; 70 int main() { 71 scanf("%d", &T); 72 while(T--) { 73 init(); 74 solve(); 75 } 76 return 0; 77 }
然后假设没有这个通配符应该怎么用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
1 /** 2 * Codeforces 3 * Problem#827 4 * Accepted 5 * Time: 311ms 6 * Memory: 68200k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 template<typename T> 13 class Complex { 14 public: 15 T r; 16 T v; 17 18 Complex(T r = 0, T v = 0):r(r), v(v) { } 19 20 Complex operator + (Complex b) { 21 return Complex(r + b.r, v + b.v); 22 } 23 24 Complex operator - (Complex b) { 25 return Complex(r - b.r, v - b.v); 26 } 27 28 Complex operator * (Complex b) { 29 return Complex(r * b.r - v * b.v, r * b.v + v * b.r); 30 } 31 32 Complex operator / (double x) { 33 return Complex(r / x, v / x); 34 } 35 }; 36 37 const int N = 1 << 21; 38 const double pi = acos(-1); 39 const double eps = 0.5; 40 41 inline void Rader(Complex<double> *f, int len) { 42 for(int i = 1, j = len >> 1, k; i < len - 1; i++) { 43 if(i < j) 44 swap(f[i], f[j]); 45 for(k = len >> 1; j >= k; j -= k, k >>= 1); 46 if(j < k) 47 j += k; 48 } 49 } 50 51 inline void fft(Complex<double> *f, int len, int sign) { 52 Rader(f, len); 53 for(int l = 2; l <= len; l <<= 1) { 54 Complex<double> wn(cos(2 * pi / l), sin(2 * pi * sign / l)), u, v; 55 int hl = l >> 1; 56 for(int i = 0; i < len; i += l) { 57 Complex<double> w(1, 0); 58 for(int j = 0; j < hl; j++, w = w * wn) { 59 u = f[i + j], v = w * f[i + j + hl]; 60 f[i + j] = u + v, f[i + j + hl] = u - v; 61 } 62 } 63 } 64 if(sign == -1) 65 for(int i = 0; i < len; i++) 66 f[i] = f[i] / len; 67 } 68 69 int n, len; 70 char str[500005]; 71 Complex<double> A[N], B[N]; 72 73 inline void init() { 74 scanf("%d", &n); 75 gets(str); 76 gets(str); 77 for(len = 1; len < (n << 1); len <<= 1); 78 memset(A, 0, sizeof(Complex<double>) * (len + 1)); 79 memset(B, 0, sizeof(Complex<double>) * (len + 1)); 80 for(int i = 0; i < n; i++) { 81 if(str[i] == 'V') 82 A[n - i - 1].r = 1; 83 else if(str[i] == 'K') 84 B[i].r = 1; 85 } 86 } 87 88 boolean bad[N]; 89 int res = 0; 90 inline void solve() { 91 fft(A, len, 1); 92 fft(B, len, 1); 93 for(int i = 0; i < len; i++) A[i] = A[i] * B[i]; 94 fft(A, len, -1); 95 memset(bad, false, sizeof(boolean) * (n + 1)); 96 // for(int i = 1; i < n; i++) 97 // if(A[n - i - 1].r >= eps || A[n + i - 1].r >= eps) 98 // bad[i] = true; 99 for(int i = 0; i < len; i++) 100 if(A[i].r >= eps) 101 bad[abs(i - n + 1)] = true; 102 for(int i = 1; i < n; i++) 103 if(!bad[i]) 104 for(int j = i << 1; j < n; j += i) 105 if(bad[j]) { 106 bad[i] = true; 107 break; 108 } 109 int res = 0; 110 for(int i = 1; i <= n; i++) 111 if(!bad[i]) 112 res++; 113 printf("%d\n", res); 114 for(int i = 1; i <= n; i++) { 115 if(!bad[i]) 116 printf("%d ", i); 117 } 118 putchar('\n'); 119 } 120 121 int T; 122 int main() { 123 scanf("%d", &T); 124 while(T--) { 125 init(); 126 solve(); 127 } 128 return 0; 129 }