题面
很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串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
题解
字符串中存在 ∗ * ∗ 号,不好哈希,我们可以换种思路:让两个串位置对应相乘,一方存在 ∗ * ∗ 号即贡献 0 ,否则只有字符相等才贡献 1 。不难想到这就是翻转后再卷积。
我们设定一个可做NTT的大质数(如998244353),把每一种字母都随机一个不同的较小数字,在串 A A A 中替换为该数字,串 B B B 中替换为该数字的逆元,然后 ∗ * ∗ 号替换为 0 。那么我们可以同时求出对应位相乘的乘积和,以及两个串之间同为字母的位置数(所有字母替换为 1 卷一遍)。可匹配的依据就是这两个值相等。
时间复杂度 O ( ( n + m ) log ( n + m ) ) O((n+m)\log(n+m)) O((n+m)log(n+m)) 。
CODE
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 300005
#define LL long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
int xchar() {
static const int maxn = 1000000;
static char b[maxn];
static int pos = 0,len = 0;
if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
if(pos == len) return -1;
return b[pos ++];
}
//#define getchar() xchar()
LL read() {
LL f = 1,x = 0;int s = getchar();
while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
if(!x) {putchar('0');return ;}
if(x<0) putchar('-'),x = -x;
return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}
const int MOD = 1004535809;
const int pmr = 3;
int n,m,s,o,k;
int fac[MAXN],inv[MAXN],invf[MAXN],pw2[MAXN];
int qkpow(int a,int b) {
int res = 1;
while(b > 0) {
if(b & 1) res = res *1ll* a % MOD;
a = a *1ll* a % MOD; b >>= 1;
}return res;
}
int rev[MAXN<<2],xm[MAXN<<2],om;
void NTT(int *s,int n,int op) {
for(int i = 1;i < n;i ++) {
rev[i] = (rev[i>>1]>>1) | ((i&1) ? (n>>1):0);
if(rev[i] < i) swap(s[rev[i]],s[i]);
}
om = qkpow(pmr,(MOD-1)/n); xm[0] = 1;
if(op < 0) om = qkpow(om,MOD-2);
for(int i = 1;i <= n;i ++) xm[i] = xm[i-1]*1ll*om%MOD;
for(int k = 2,t = n>>1;k <= n;k <<= 1,t >>= 1) {
for(int j = 0;j < n;j += k) {
for(int i = j,l = 0;i < j+(k>>1);i ++,l += t) {
int A = s[i],B = s[i+(k>>1)];
s[i] = (0ll+A + xm[l]*1ll*B) % MOD;
s[i+(k>>1)] = ((0ll+A-xm[l]*1ll*B) % MOD+MOD)%MOD;
}
}
}
if(op < 0) {
int invn = qkpow(n,MOD-2);
for(int i = 0;i < n;i ++) s[i] = s[i] *1ll* invn % MOD;
}return ;
}
int A[MAXN<<2],B[MAXN<<2],A2[MAXN<<2],B2[MAXN<<2];
int nb[26];
char a[MAXN],b[MAXN];
int main() {
fac[0]=fac[1]=inv[0]=inv[1]=invf[0]=invf[1]=1;
for(int i = 2;i <= MAXN-5;i ++) {
fac[i] = fac[i-1] *1ll* i % MOD;
inv[i] = (MOD-inv[MOD%i]) *1ll* (MOD/i) % MOD;
invf[i] = invf[i-1] *1ll* inv[i] % MOD;
}
int rd = 81097;
for(int i = 0;i < 26;i ++) nb[i] = (rd = rd*233ll%(MAXN-5));
n = read();m = read();
scanf("%s",a + 1); scanf("%s",b + 1);
for(int i = 1;i <= n;i ++) {
if(a[i] != '*') {
A[i] = 1;
A2[i] = nb[a[i]-'a'];
}
}
for(int j = 1;j <= m;j ++) {
if(b[m-j+1] != '*') {
B[j] = 1;
B2[j] = inv[nb[b[m-j+1]-'a']];
}
}
int le = 1;while(le <= n+m) le <<= 1;
NTT(A,le,1); NTT(B,le,1); NTT(A2,le,1); NTT(B2,le,1);
for(int i = 0;i < le;i ++) A[i] = A[i] *1ll* B[i] % MOD, A2[i] = A2[i] *1ll* B2[i] % MOD;
NTT(A,le,-1); NTT(A2,le,-1);
queue<int> qu;
for(int i = 1;i <= m-n+1;i ++) {
int ad = m-i+2;
if(A2[ad] == A[ad]) qu.push(i);
}
AIput(qu.size(),'\n');
while(!qu.empty()) {
int t = qu.front();qu.pop();
AIput(t,qu.empty() ? '\n':' ');
}
return 0;
}