您的位置:首页 > 其它

bzoj4259残缺的字符串FFT解题报告

2017-12-16 17:54 246 查看

4259: 残缺的字符串

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 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 7

a*b

aebr*ob

Sample Output

2

1 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: