【ABC196F】Substring 2(多项式乘法)

我竟然能在 AT 当场做出 F 题!

哦,是 ABC 啊,没事了。


以下的字符串均从 1 1 1 开始记位。以下设 S i S_i Si 表示字符串 S S S 的第 i i i 位, S ( l , r ) S(l,r) S(l,r) 表示字符串 S S S 的第 l l l 位到第 r r r 位组成的子串,也可以表示字符串 S S S 的第 l l l 位到第 r r r 位组成的序列

f i , j f_{i,j} fi,j 表示 S S S 串从位置 i i i 往后 j j j 个字符所组成的串 和 T T T 串从位置 1 1 1 往后 j j j 个字符所组成的串 有多少位不相等,即 S ( i , i + j − 1 ) S(i,i+j-1) S(i,i+j1) T ( 1 , j ) T(1,j) T(1,j) 的相差字符个数。

n = ∣ S ∣ , m = ∣ T ∣ n=|S|,m=|T| n=S,m=T,那么我们要求的即为:

min ⁡ i = 1 n − m + 1 f i , m \min\limits_{i=1}^{n-m+1} f_{i,m} i=1minnm+1fi,m

显然有转移:

f i , 0 = 0 f i , j = f i , j − 1 + [ S i + j − 1 ≠ T j ] \begin{aligned} &f_{i,0}=0\\ &f_{i,j}=f_{i,j-1}+\big[S_{i+j-1}\neq T_{j}\big] \end{aligned} fi,0=0fi,j=fi,j1+[Si+j1=Tj]

以下定义两个长度相等的序列 A , B A,B A,B 相加表示 A A A B B B 各位相加后组成的序列。即如果 C = A + B C=A+B C=A+B,那么 C i = A i + B i C_i=A_i+B_i Ci=Ai+Bi

两个序列相减的定义类似。

一个常数乘一个序列的定义类似。

设序列 F j F_j Fj 表示长度为 n − m + 1 n-m+1 nm+1 的序列 f 1 , j , f 2 , j , ⋯   , f n − m + 1 , j f_{1,j},f_{2,j},\cdots,f_{n-m+1,j} f1,j,f2,j,,fnm+1,j,那么我们要求的就是序列 F m F_m Fm 每一位上的值的最小值。

设序列 G j G_j Gj 表示长度为 n − m + 1 n-m+1 nm+1 的序列 [ S 1 + j − 1 ≠ T j ] , [ S 2 + j − 1 ≠ T j ] , ⋯   , [ S ( n − m + 1 ) + j − 1 ≠ T j ] [S_{1+j-1}\neq T_j],[S_{2+j-1}\neq T_j],\cdots,[S_{(n-m+1)+j-1}\neq T_j] [S1+j1=Tj],[S2+j1=Tj],,[S(nm+1)+j1=Tj]

那么根据之前得到的状态转移方程,有:

F j = F j − 1 + G j F_{j}=F_{j-1}+G_{j} Fj=Fj1+Gj

考虑转化 G j G_j Gj

首先,在 a , b a,b a,b 都是 0 0 0 1 1 1 时,有 [ a ≠ b ] = a ⊕ b [a\neq b]=a \oplus b [a=b]=ab。( ⊕ \oplus 指异或运算)

那么 G j G_j Gj 就可以变成序列 S 1 + j − 1 ⊕ T j , S 2 + j − 1 ⊕ T j , ⋯   , S ( n − m + 1 ) + j − 1 ⊕ T j S_{1+j-1}\oplus T_j,S_{2+j-1}\oplus T_j,\cdots,S_{(n-m+1)+j-1}\oplus T_j S1+j1Tj,S2+j1Tj,,S(nm+1)+j1Tj

再进一步,当 T j = 0 T_j=0 Tj=0 时, G j G_j Gj 就是 S ( j , j + n − m ) S(j,j+n-m) S(j,j+nm);当 T j = 1 T_j=1 Tj=1 时, G j G_j Gj 就是 S ( j , j + n − m ) S(j,j+n-m) S(j,j+nm) 每一位都取反后形成的序列。

更进一步,注意到 S ( j , j + n − m ) S(j,j+n-m) S(j,j+nm) 每一位都取反后形成的序列其实就是序列 ( 11...1 ) n − m + 1 − S ( j , j + n − m ) (11...1)_{n-m+1}-S(j,j+n-m) (11...1)nm+1S(j,j+nm)。(这里的 ( 11...1 ) n − m + 1 (11...1)_{n-m+1} (11...1)nm+1 指的是一个长度为 n − m + 1 n-m+1 nm+1 的全为 1 1 1 的序列)。

那么有:

F j = F j − 1 + { S ( j , j + n − m ) if ⁡ T j = 0 ( 11...1 ) n − m + 1 − S ( j , j + n − m ) if ⁡ T j = 1 F_j=F_{j-1}+ \begin{cases} S(j,j+n-m)&\operatorname{if }T_j=0\\ (11...1)_{n-m+1}-S(j,j+n-m)&\operatorname{if }T_j=1 \end{cases} Fj=Fj1+{S(j,j+nm)(11...1)nm+1S(j,j+nm)ifTj=0ifTj=1

那么:

F m = ∑ j = 1 m { S ( j , j + n − m ) if ⁡ T j = 0 ( 11...1 ) n − m + 1 − S ( j , j + n − m ) if ⁡ T j = 1 F_m=\sum_{j=1}^m\begin{cases} S(j,j+n-m)&\operatorname{if }T_j=0\\ (11...1)_{n-m+1}-S(j,j+n-m)&\operatorname{if }T_j=1 \end{cases} Fm=j=1m{S(j,j+nm)(11...1)nm+1S(j,j+nm)ifTj=0ifTj=1

我们可以把 ( 11...1 ) n − m + 1 (11...1)_{n-m+1} (11...1)nm+1 抽出来最后算:假设 T T T 里面有 t o t tot tot 位是 1 1 1,那么:

F m = t o t × ( 11...1 ) n − m + 1 + ∑ j = 1 m ( − 1 ) T j S ( j , j + n − m ) F_m=tot\times (11...1)_{n-m+1}+\sum_{j=1}^m(-1)^{T_j}S(j,j+n-m) Fm=tot×(11...1)nm+1+j=1m(1)TjS(j,j+nm)

现在我们只需要计算 ∑ j = 1 m ( − 1 ) T j S ( j , j + n − m ) \sum\limits_{j=1}^m(-1)^{T_j}S(j,j+n-m) j=1m(1)TjS(j,j+nm) 即可。

这个式子可以理解成为:有一个长度为 n − m + 1 n-m+1 nm+1 的滑动窗口在 S S S 上从左往右移动,每移到某一个位置就把当前记录的答案序列加上/减去滑动窗口内框住的序列。

由于这个滑动窗口是一直在 S S S 上的,所以我们可以用多项式来优化这个操作:

A ( x ) = S 1 x + S 2 x 2 + ⋯ + S n x n A(x)=S_1x+S_2x^2+\cdots+S_nx^n A(x)=S1x+S2x2++Snxn

我们考虑求出一个多项式 C ( x ) C(x) C(x),然后用 C ( x ) C(x) C(x) 的第 m m m 位到第 n n n 位(即 x m x^m xm x n x^n xn 的系数)这 n − m + 1 n-m+1 nm+1 位来表示 ∑ j = 1 m ( − 1 ) T j S ( j , j + n − m ) \sum\limits_{j=1}^m(-1)^{T_j}S(j,j+n-m) j=1m(1)TjS(j,j+nm) 计算后得到的长度为 n − m + 1 n-m+1 nm+1 的序列。(注意, C ( x ) C(x) C(x) 的其他位算出来是啥我们不用管,只用保证这 n − m + 1 n-m+1 nm+1 位是对的就行)

容易得到:
C ( x ) = ∑ j = 1 m ( − 1 ) T j ( S j x j + ⋯ + S j + n − m x j + n − m ) x m − j = ∑ j = 1 m ( − 1 ) T j A ( x ) x m − j = A ( x ) × ( ∑ j = 1 m ( − 1 ) T j x m − j ) \begin{aligned} C(x)=&\sum_{j=1}^m (-1)^{T_j} \left(S_jx^j+\cdots+S_{j+n-m}x^{j+n-m}\right)x^{m-j}\\ =&\sum_{j=1}^m (-1)^{T_j} A(x) x^{m-j}\\ =&A(x)\times \left(\sum_{j=1}^m (-1)^{T_j} x^{m-j}\right) \end{aligned} C(x)===j=1m(1)Tj(Sjxj++Sj+nmxj+nm)xmjj=1m(1)TjA(x)xmjA(x)×(j=1m(1)Tjxmj)
乘号两边都是多项式,用 FFT 或 NTT 优化多项式乘法即可。

代码如下:

#include<bits/stdc++.h>

#define LN 22
#define N 1000010
#define INF 0x7fffffff

using namespace std;

namespace modular
{
	const int mod=998244353;
	inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
	inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
	inline int mul(int x,int y){return 1ll*x*y%mod;}
}using namespace modular;

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<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

inline int poww(int a,int b)
{
    int ans=1;
    while(b)
    {
        if(b&1) ans=mul(ans,a);
        a=mul(a,a);
        b>>=1;
    }
    return ans;
}

int n,m;
int a[N<<3],b[N<<3];
int rev[N<<3],w[LN][N<<3][2];
char s[N],t[N];

void init(int limit)
{
    for(int bit=0,mid=1;mid<limit;bit++,mid<<=1)
    {
        int len=mid<<1;
        int gn=poww(3,(mod-1)/len);
        int ign=poww(gn,mod-2);
        int g=1,ig=1;
        for(int j=0;j<mid;g=mul(g,gn),ig=mul(ig,ign),j++)
            w[bit][j][0]=g,w[bit][j][1]=ig;
    }
}
 
void NTT(int *a,int limit,int opt)
{
    opt=(opt<0);
    for(int i=0;i<limit;i++)
        rev[i]=(rev[i>>1]>>1)|((i&1)*(limit>>1));
    for(int i=0;i<limit;i++)
        if(i<rev[i]) swap(a[i],a[rev[i]]);
    for(int bit=0,mid=1;mid<limit;bit++,mid<<=1)
    {
        for(int i=0,len=mid<<1;i<limit;i+=len)
        {
            for(int j=0;j<mid;j++)
            {
                int x=a[i+j],y=mul(w[bit][j][opt],a[i+mid+j]);
                a[i+j]=add(x,y),a[i+mid+j]=dec(x,y);
            }
        }
    }
    if(opt)
    {
        int tmp=poww(limit,mod-2);
        for(int i=0;i<limit;i++)
            a[i]=mul(a[i],tmp);
    }
}

int main()
{
	scanf("%s%s",s+1,t+1);
	n=strlen(s+1),m=strlen(t+1);
	for(int i=1;i<=n;i++) a[i]=s[i]-'0';
	int tot=0;
	for(int i=1,j=m-1;i<=m;i++,j--)
	{
		if(t[i]=='0') b[j]=1;
		else b[j]=-1,tot++;
	}
	int limit=1;
	while(limit<=(n<<1)) limit<<=1;
	init(limit);
	NTT(a,limit,1),NTT(b,limit,1);
	for(int i=0;i<limit;i++) a[i]=mul(a[i],b[i]);
	NTT(a,limit,-1);
	int ans=INF;
	for(int i=m;i<=n;i++)
		ans=min(ans,add(a[i],tot));
	printf("%d\n",ans);
	return 0;
}
/*
0101010
1010101
*/
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值