[Luogu P4173] [BZOJ 4259] 残缺的字符串

洛谷传送门
BZOJ传送门

题目描述

很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串 A A A B B B,其中 A A A串长度为 m m m B B B串长度为 n n n。可当你现在再次碰到这两个串时,这两个串已经老化了,每个串都有不同程度的残缺。

你想对这两个串重新进行匹配,其中 A A A为模板串,那么现在问题来了,请回答,对于 B B B的每一个位置 i i i,从这个位置开始连续 m m m个字符形成的子串是否可能与 A A A串完全匹配?

输入输出格式

输入格式:

第一行包含两个正整数 m , n m,n m,n,分别表示 A A A串和 B B B串的长度。

第二行为一个长度为 m m m的字符串 A A A

第三行为一个长度为 n n n的字符串 B B B

两个串均仅由小写字母和 ∗ * 号组成,其中 ∗ * 号表示相应位置已经残缺。

输出格式:

第一行包含一个整数 k k k,表示 B B B串中可以完全匹配 A A A串的位置个数。

k > 0 k > 0 k>0,则第二行输出 k k k个正整数,从小到大依次输出每个可以匹配的开头位置(下标从 1 1 1开始)。

输入输出样例

输入样例#1:
3 7
a*b
aebr*ob
输出样例#1:
2
1 5

说明

100%的数据满足 1 ≤ m ≤ n ≤ 300000 1 \leq m \leq n \leq 300000 1mn300000

解题分析

这种通配符匹配的问题 D P DP DP也能做, 但显然要跑一年才能跑出来… 因为字符串不全自动机也就 G G GG GG了…

所以我们考虑换一种思路, 如果没有通配符匹配的串会满足
∑ i = 1 m ( A [ i ] − B [ p o s + i ] ) 2 = 0 \sum_{i=1}^{m}(A[i]-B[pos+i])^2=0 i=1m(A[i]B[pos+i])2=0
那么如果有通配符的话实际上其的权值设为0就行, 然后我们魔改一下这个式子就行了:
∑ i = 1 m A [ i ] × ( A [ i ] − B [ p o s + i ] ) 2 × B [ i ] = 0 \sum_{i=1}^{m}A[i]\times(A[i]-B[pos+i])^2\times B[i]=0 i=1mA[i]×(A[i]B[pos+i])2×B[i]=0
但这玩意还是没法做, 我们把 A A A翻转一下上面那个式子就变成了一个卷积的形式, 做 7 7 7 F F T FFT FFT就好了。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <cctype>
#define R register
#define IN inline
#define W while
#define db double
#define MX 2400500
const db PI = std::acos(-1.0);
struct Complex {db im, re;} a[MX], b[MX], c[MX];
IN Complex operator * (const Complex &x, const Complex &y)
{return {x.im * y.re + x.re * y.im, x.re * y.re - x.im * y.im};}
IN Complex operator + (const Complex &x, const Complex &y)
{return {x.im + y.im, x.re + y.re};}
IN Complex operator - (const Complex &x, const Complex &y)
{return {x.im - y.im, x.re - y.re};}
IN Complex operator * (const Complex &x, const db &y)
{return {x.im * y, x.re * y};}
char tar[MX], mod[MX];
int rev[MX], ans[MX];
db v1[MX], v2[MX];
int tot, sum, lg, cnt, len1, len2;
IN void FFT(Complex *dat, const int &typ)
{
	for (R int i = 1; i < tot; ++i) if(rev[i] < i) std::swap(dat[rev[i]], dat[i]);
	R int cur, seg, bd, step, now; Complex deal, base, buf1, buf2;
	for (seg = 1; seg < tot; seg <<= 1)
	{
		step = seg << 1; base = {std::sin(PI / seg) * typ, std::cos(PI / seg)};
		for (now = 0; now < tot; now += step)
		{
			deal = {0, 1}; bd = now + seg;
			for (cur = now; cur < bd; ++cur, deal = deal * base)
			{
				buf1 = dat[cur], buf2 = dat[cur + seg] * deal;
				dat[cur] = buf1 + buf2, dat[cur + seg] = buf1 - buf2;
			}
		}
	}
}
int main(void)
{
	scanf("%d%d", &len1, &len2);
	scanf("%s%s", tar, mod);
	for (R int i = 0; i < len1; ++i) v1[len1 - i - 1] = (tar[i] == '*') ? 0 : tar[i] - 'a' + 1;
	for (R int i = 0; i < len2; ++i) v2[i] = (mod[i] == '*') ? 0 : mod[i] - 'a' + 1;
	sum = len1 + len2;
	for (tot = 1; tot <= sum; tot <<= 1, ++lg); tot <<= 1, ++lg;
	for (R int i = 1; i < tot; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (lg - 1));
	for (R int i = 0; i < tot; ++i) a[i] = {0, v1[i] * v1[i] * v1[i]}, b[i] = {0, v2[i]};
	FFT(a, 1), FFT(b, 1);
	for (R int i = 0; i < tot; ++i) c[i] = a[i] * b[i];
	for (R int i = 0; i < tot; ++i) a[i] = {0, v1[i] * v1[i]}, b[i] = {0, v2[i] * v2[i]};
	FFT(a, 1), FFT(b, 1);
	for (R int i = 0; i < tot; ++i) c[i] = c[i] - a[i] * b[i] * 2.0;
	for (R int i = 0; i < tot; ++i) a[i] = {0, v1[i]}, b[i] = {0, v2[i] * v2[i] * v2[i]};
	FFT(a, 1), FFT(b, 1);
	for (R int i = 0; i < tot; ++i) c[i] = c[i] + a[i] * b[i];
	FFT(c, -1); int bd = len2 - len1;
	for (R int i = 0; i <= bd; ++i) if(fabs(c[i + len1 - 1].re) < 0.5) ans[++cnt] = i + 1;
	printf("%d\n", cnt);
	for (R int i = 1; i <= cnt; ++i) printf("%d ", ans[i]);
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值