1. 程式人生 > >[Luogu P4173] [BZOJ 4259] 殘缺的字串

[Luogu P4173] [BZOJ 4259] 殘缺的字串

洛谷傳送門

題目描述

很久很久以前,在你剛剛學習字串匹配的時候,有兩個僅包含小寫字母的字串AABB,其中AA串長度為mmBB串長度為nn。可當你現在再次碰到這兩個串時,這兩個串已經老化了,每個串都有不同程度的殘缺。

你想對這兩個串重新進行匹配,其中AA為模板串,那麼現在問題來了,請回答,對於BB的每一個位置ii,從這個位置開始連續mm個字元形成的子串是否可能與AA串完全匹配?

輸入輸出格式

輸入格式:

第一行包含兩個正整數m,nm,n,分別表示AA串和BB串的長度。

第二行為一個長度為mm的字串AA

第三行為一個長度為nn的字串BB

兩個串均僅由小寫字母和

*號組成,其中*號表示相應位置已經殘缺。

輸出格式:

第一行包含一個整數kk,表示BB串中可以完全匹配AA串的位置個數。

k>0k > 0,則第二行輸出kk個正整數,從小到大依次輸出每個可以匹配的開頭位置(下標從11開始)。

輸入輸出樣例

輸入樣例#1:

3 7
a*b
aebr*ob

輸出樣例#1:

2
1 5

說明

100%的資料滿足1mn3000001 \leq m \leq n \leq 300000

解題分析

這種萬用字元匹配的問題DPDP也能做, 但顯然要跑一年才能跑出來… 因為字串不全自動機也就GGGG了…

所以我們考慮換一種思路, 如果沒有萬用字元匹配的串會滿足

i=1m(A[i]B[pos+i])2=0 \sum_{i=1}^{m}(A[i]-B[pos+i])^2=0 那麼如果有萬用字元的話實際上其的權值設為0就行, 然後我們魔改一下這個式子就行了: i=1mA[i]×(A[i]B[pos+i])2×B[i]=0 \sum_{i=1}^{m}A[i]\times(A[i]-B[pos+i])^2\times B[i]=0 但這玩意還是沒法做, 我們把AA翻轉一下上面那個式子就變成了一個卷積的形式, 做77FFTFFT就好了。

程式碼如下:

#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]);
}