给一个文本串,一个模式串,2e5,输出文本串的哪些位置和模式串荣誉匹配。
A和B荣誉匹配是指,A和B长度相同,并且如果存在若干个没有重复字母的荣誉转换<c1,c2>,把A中的每个c1都换成c2,每个c2换成c1,然后它和B相等。
把文本串和模式串分别按字符分解成 个字符串,所谓按字符分解是指:
- 第一个串,在原串是 的位置上是1,否则是0
- 第二个串,在原串是 的位置上是1,否则是0
- 以此类推。
对 个模式串建立AC自动机,把26个文本串同时放在上面跑,每个时刻,可以得到哪些文本串与哪些模式串相匹配。
记录所有使分解后的 不为全0的字符,称为非空分解串。对于一个非空的 分解串,要么没有串能和匹配,要么有唯一一个非空的 分解串和它匹配。
对于所有非空分解串 ,必须满足:
- 有一个 与其匹配。
- 或者 与 匹配。
- 否则本位置荣誉匹配失败。
想了不少时间空串怎么处理,最后发现不要处理就行了。把所有非空串处理掉,空串自然就没了。
以前还没有这样按字符分解然后插入,感觉很巧妙。
/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 5200016, MOD = 1000000007;
//Trie树(图)/是否为终结点/失配链接/后缀链接,都是节点的属性
int sz = 0;
int ch[M][2], ed[M], fail[M], last[M];
// 向Trie树中尝试插入一个模式串, 返回插入后的节点编号
void insert(const char *s, int id)
{
int u = 0;
for(int i = 0; s[i]; i++)
{
int &v = ch[u][s[i] - '0'];
if(!v) v=++sz;
u = v;
}
ed[u] = id;
}
// 构建AC自动机
void build()
{
queue<int> q;
for(int v:ch[0]) if(v)
fail[v] = 0, q.push(v);
while(!q.empty())
{
int u = q.front(); q.pop();
for(int i = 0; i < 2; i++)
{
int &v = ch[u][i];
if(v)
{
q.push(v);
fail[v] = ch[fail[u]][i];
last[v] = ed[fail[v]] ? fail[v] : last[fail[v]]; //后缀链接
}
else v = ch[fail[u]][i]; //建立trie图
}
}
}
char tex[200016], pat[200016], tpat[200016];
int has[M]; //每个字母是否有,字母编号是1到26
int main(void)
{
#ifdef _LITTLEFALL_
freopen("in.txt","r",stdin);
#endif
int n = read(), m = read();
scanf("%s %s", tex, pat);
for(int c=1; c<=26; ++c)
{
for(int i=0; i<m; ++i)
{
if(pat[i]==c-1+'a')
{
tpat[i] = '1';
has[c] = 1;
}
else
tpat[i] = '0';
}
if(has[c]) insert(tpat, c); //只插入非空串
}
build();
vector<int> ans;
int now[30] = {}, mat[30] = {};
for(int i=0; i<n; ++i)
{
memset(mat, 0, sizeof(mat));
for(int c=1; c<=26; ++c)
{
int trs = (tex[i]==c-1+'a') ? 1 : 0;
now[c] = ch[now[c]][trs];
int match = ed[now[c]];
if(match) //匹配了一个非空串
mat[match] = c;
}
int suc = 1;
for(int c=1; c<=26; ++c) if(has[c])
{
//printf("%c %c\n",c+'a'-1,mat[c]+'a'-1 );
if(!mat[c])
{
suc = 0;
break;
}
int rc = mat[c];
if(mat[rc]==c || !has[rc]) //正好匹配(包括相等)或者空
continue;
else
{
suc = 0;
break;
}
}
//printf("i=%d: ",i );
if(suc)
{
//printf("suc %d",i-m+2);
ans.push_back(i-m+2);
}
//printf("\n");
}
printf("%d\n",ans.size() );
for(auto x:ans)
printf("%d ",x );
return 0;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}