Description
给出两个字符串S和T
定义两个等长的字符串A和B之间的距离为:
每次操作可以选择两个字符c1和c2,将两个字符串中的所有c1替换为c2,这样将A和B变为相等的最小操作次数。
求S的每个长度为|T|的子串和T之间的距离。
n<=125000,保证S和T只含有小写字母a~f
Solution
先考虑怎么求一对字符串的距离
对于每个字符c开一个点,从A中每个字符向B中相同位置的字符连一条无向边
答案就是6-联通块个数,因为不同联通块之间显然不需要耗费操作次数,同一联通块可以用n-1次完成
接下来就很简单了,最多只有30种边,枚举S和T中哪两个字符匹配,接下来就变成了字符串配对问题
FFT即可
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=lst[a];i;i=nxt[i])
using namespace std;
typedef long long ll;
const int N=4e5+5,Mo=998244353;
int pwr(int x,int y) {
int z=1;
for(;y;y>>=1,x=(ll)x*x%Mo)
if (y&1) z=(ll)z*x%Mo;
return z;
}
void mod(int &x) {x=x>=Mo?x-Mo:x;}
int n,m,len,lg,inv,t[N],f[N],g[N],h[N],W[N],e[N][6][6];
char S[N],T[N];
bool vis[6];
void pre(int N) {
for(len=1,lg=0;len<N;len<<=1) lg++;
W[0]=1;W[1]=pwr(3,(Mo-1)/len);
fo(i,2,len) W[i]=(ll)W[i-1]*W[1]%Mo;
inv=pwr(len,Mo-2);
}
void DFT(int *a,int flag) {
for(int i=0;i<len;i++) {
int p=0;
for(int j=i,k=0;k<lg;k++,j>>=1) p=(p<<1)+(j&1);
t[p]=a[i];
}
for(int m=2;m<=len;m<<=1) {
int half=m/2,times=len/m;
for(int i=0;i<half;i++) {
int w=(flag>0)?W[i*times]:W[len-i*times];
for(int j=i;j<len;j+=m) {
int u=t[j],v=(ll)t[j+half]*w%Mo;
mod(t[j]=u+v);
mod(t[j+half]=u+Mo-v);
}
}
}
for(int i=0;i<len;i++) a[i]=t[i];
if (flag==-1) for(int i=0;i<len;i++) a[i]=(ll)a[i]*inv%Mo;
}
void dfs(int x,int n) {
vis[x]=1;
fo(i,0,5) if (!vis[i]&&e[n][x][i]) dfs(i,n);
}
int main() {
scanf("%s",S);n=strlen(S);
scanf("%s",T);m=strlen(T);
fo(i,0,m/2-1) swap(T[i],T[m-i-1]);
pre(n+m);
fo(a,0,5)
fo(b,0,5) {
if (a==b) continue;
fo(i,0,len-1) f[i]=g[i]=0;
fo(i,0,n-1) f[i]=S[i]-'a'==a;
fo(i,0,m-1) g[i]=T[i]-'a'==b;
DFT(f,1);DFT(g,1);
fo(i,0,len-1) h[i]=(ll)f[i]*g[i]%Mo;
DFT(h,-1);
fo(i,m-1,n-1) {
e[i][a][b]|=h[i]>0;
e[i][b][a]|=h[i]>0;
}
}
fo(i,m-1,n-1) {
int ans=6;
fo(j,0,5) vis[j]=0;
fo(j,0,5) if (!vis[j]) dfs(j,i),ans--;
printf("%d ",ans);
}
return 0;
}