bzoj4259残缺的字符串FFT解题报告
2017-12-16 17:54
246 查看
4259: 残缺的字符串
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 869 Solved: 208
[Submit][Status][Discuss]
Description
很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串A和B,其中A串长度为m,B串长度为n。可当你现在再次碰到这两个串时,这两个串已经老化了,每个串都有不同程度的残缺。你想对这两个串重新进行匹配,其中A为模板串,那么现在问题来了,请回答,对于B的每一个位置i,从这个位置开始连续m个字符形成的子串是否可能与A串完全匹配?
Input
第一行包含两个正整数m,n(1<=m<=n<=300000),分别表示A串和B串的长度。第二行为一个长度为m的字符串A。
第三行为一个长度为n的字符串B。
两个串均仅由小写字母和*号组成,其中*号表示相应位置已经残缺。
Output
第一行包含一个整数k,表示B串中可以完全匹配A串的位置个数。若k>0,则第二行输出k个正整数,从小到大依次输出每个可以匹配的开头位置(下标从1开始)。
Sample Input
3 7a*b
aebr*ob
Sample Output
21 5
HINT
Source
By Claris[Submit][Status][Discuss]
HOME Back
한국어 中文 فارسی English ไทย
题目大意:
带*号字符串匹配(*可以配任意字母)
神题啊,没想到fft还可以干这种事情。
首先不考虑*,对于某个在第k位匹配的字符串,一定满足
sum(Ai+k-Bi)^2=0(小写字母为下标)
然后嘞,*那一位显然要强制转换为0,那么我们就令*那一位为0,然后只要满足
sum(Ai+k-Bi)^2 * Ai+k * Bi=0就可以了。
我们将上式化简,得到
sum(Ai+k^3 *Bi + Ai+k * Bi ^ 3 - 2 * Ai+k ^ 2 * Bi ^ 2)=0
我们要将上式转化为若干个多项式乘法,显然只和Ai+k和Bi有关。
考虑到普通多项式乘法把Ai+k*Bi项的贡献统计到2i+k位上
而题设希望所有Ai+k和Bi相关计算都统计到同一位上
只需对B序列进行适当转化,使得所有的i+k和i位统计到同一位上即可
假设转化后对应关系为f(i)
那么有对于任意的i,i+f(i)=a(a为定值)
于是有f(i)=a-i
至于a具体是什么,看个人的具体喜好,反正我是令a=n,也就是把整个B串反转了。
这样的话,只需要进行三次多项式乘法之后,检验符号条件的为0的项即可,问题就圆满地解决了。
大体思路过程:字符串匹配->数学表达式->多项式乘法,其中有通过贡献考虑问题的和模型转化的类比思想
代码贼舒服
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
using namespace std;
const int N = 6e5 + 6e3;
const double pi = acos(-1.0);
struct cp {
double r, i;
cp(double real = 0, double imag = 0) : r(real), i(imag) {}
cp operator * (cp a) {return cp(r * a.r - i * a.i, i * a.r + r * a.i);}
cp operator * (double a) {return cp(r * a, i * a);}
cp operator + (cp a) {return cp(r + a.r, i + a.i);}
cp operator - (cp a) {return cp(r - a.r, i - a.i);}
}A
, B
, C
;
int R
, a
, b
, ans
, bin[40], top, m, n, L, k;
char sa
, sb
;
void FFT(cp *F, int f) {
for(int i = 0;i < k; ++i) if(i < R[i]) swap(F[i], F[R[i]]);
for(int i = 1;i < k; i <<= 1) {
cp wn(cos(pi / i), f * sin(pi / i));
for(int j = 0; j < k; j += (i << 1)) {
cp w(1, 0);
for(int l = 0;l < i; ++l, w = w * wn) {
cp x = F[j + l], y = w * F[j + l + i];
F[j + l] = x + y; F[j + l + i] = x - y;
}
}
}
if(!(~f)) for(int i = 0;i < k; ++i) F[i].r /= k;
}
int mul(int x, int cnt) {int ret = 1; while(cnt--) ret = ret * x; return ret;}
void work(int p) {
for(int i = 0;i < k; ++i) A[i] = cp(mul(a[i], p), 0), B[i] = cp(mul(b[i], 4 - p), 0);
FFT(A, 1); FFT(B, 1); bool f = p == 2;
if(f) for(int i = 0;i < k; ++i) C[i] = C[i] - A[i] * B[i] * 2;
else for(int i = 0;i < k; ++i) C[i] = C[i] + A[i] * B[i];
}
int main() {
scanf("%d%d%s%s", &m, &n, sa, sb);
for(int i = 0;i < (m >> 1); ++i) swap(sa[i], sa[m - i - 1]);
for(int i = 0;i < m; ++i) a[i] = sa[i] == '*' ? 0 : sa[i] - 'a' + 1;
for(int i = 0;i < n; ++i) b[i] = sb[i] == '*' ? 0 : sb[i] - 'a' + 1;
for(k = 1;k < n + m; k <<= 1, ++L) ;
for(int i = 0;i < k; ++i) R[i] = (R[i >> 1] >> 1) | ((i & 1) << L - 1);
for(int i = 1;i <= 3; ++i) work(i);
FFT(C, -1);
for(int i = m - 1;i < n; ++i) if(C[i].r < 0.5) ans[++top] = i - m + 2;
printf("%d\n", top);
for(int i = 1;i <= top; ++i) printf("%d ", ans[i]);
puts("");
return 0;
}
相关文章推荐
- BZOJ3790 神奇项链 解题报告【字符串】【Manacher】【树状数组】【数据结构优化DP】
- 九度OJ1105字符串的反码解题报告
- 简单字符串比较 ――解题报告
- 简单字符串排序解题报告
- 字符串处理--HDU第1131解题报告
- POJ3974 Palindrome 解题报告【字符串】【Manacher】
- HDU3068 最长回文串 解题报告【字符串】【Manacher】
- Leetcode 383. Ransom Note 构造字符串 解题报告
- 简单字符串排序解题报告
- 串结构练习——字符串连接 解题报告
- 解题报告: 51nod 1028 大数乘法 V2 FFT
- 字符串处理--HDU第1073解题报告
- 字符串题目推荐及解题报告【转】
- 串结构练习――字符串连接 解题报告
- POJ 1016(自总结数 字符串处理) 解题报告
- (解题报告)HDU2017---字符串统计
- BZOJ 2351 [BeiJing 2011] 字符串Hash 解题报告
- [NOIP2007] 字符串的展开-解题报告
- HDU3695 Computer Virus on Planet Pandora 解题报告【字符串】【AC自动机】
- NIT-OJ-1022-展开字符串-解题报告