1 条题解

  • 0
    @ 2025-8-24 23:03:38

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar chenxi2009
    身如柳絮随风扬。|粉福见专栏。|红名且勾支持互

    搬运于2025-08-24 23:03:38,当前版本为作者最后更新于2024-09-11 15:32:07,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    Upd 2024.11.26:压行改进码风

    专栏沉浸式阅读

    闲话:怎么这么多写矩阵乘法的呢?那就让我来给一个通俗的写法吧…

    这是一篇保姆级的简单题解。

    前置知识:组合数学,一点动态规划思想,搜索,乘法逆元

    思路

    n=S,m=Tn=\vert S\vert,m=\vert T\vert
    首先我们可以在 O(nm)O(nm) 的时间内求出 SS 的子序列中等于 TT 的序列数量。

    如果你不想思考,这里有思考过程:

    fi,jf_{i,j}S1iS_{1\cdots i} 的子序列中等于 T1jT_{1\cdots j} 的序列数量。
    易得递推:

    $$f_{i,j}=f_{i-1,j}+\sum_{k=1}^{i-1}f_{k,j-1} (S_i=T_j) $$fi,j=fi1,j(SiTj)f_{i,j}= f_{i-1,j} (S_i\ne T_j)

    复杂度 O(n2m)O(n^2m)

    gi,j=k=1jfi,kg_{i,j}=\sum_{k=1}^{j}f_{i,k}

    易得新的递推式:

    gi,j=gi1,j+gi1,j1(Si=Tj)g_{i,j}=g_{i-1,j}+g_{i-1,j-1}(S_i=T_j) gi,j=gi1,j(SiTj)g_{i,j}=g_{i-1,j}(S_i\ne T_j)

    复杂度 O(nm)O(nm)
    第二重循环倒序可以压掉第一维。空间复杂度 O(m)O(m)

    继续正文

    用这个方法,我们可以得出 TT 的所有子串在 SS 里面对应的子序列个数,复杂度为 O(nm3)O(nm^3)

    这个时候,我们已经离答案非常接近了。
    因为我们发现对于每一个重复 kk 次的 SS 构成的字符串里等同于 TT 的子序列,都是由TT 分成若干个子串,在不同位置的 SS 中取了分别等同于各个子串的子序列构成的。

    样例理解

    以题目样例 #1 为例:

    2
    stocyhorz
    cyh
    

    我们可以把 TT 分成一个子串 T1,2,3T_{1,2,3},在第一个 SS 中取该子串和在第二个 SS 中取该子串,有 2 种方案;
    我们可以把 TT 分成两个子串 T1,2,T3T_{1,2},T_3,即 cyh,在第一个 SS 中取第一个子串,第二个 SS 中取第二个子串,有 1 种方案;
    我们可以把 TT 分成两个子串 T1,T2,3T_1,T_{2,3},即 cyh,在第一个 SS 里面取第一个子串,第二个 SS 中取第二个子串,有 1 种方案;
    我们可以把 TT 分成三个子串,但是我们没有足够的 SS 去分配三个子串,因而没有方案。
    综上所述,共有 4 种方案。

    以题目输出 #2 为例:

    4
    c
    ccc
    

    唯一有方案的分割方式是把 T=T=ccc 分割成三个子串 c,分配给 k=k= 4S=S=c,有 4 种方案。

    如果你还没有想到正解?

    从最小的问题开始思考:

    不妨先假定我们已经确定了一种分配方案,将 TT 分割为了 pp 个子串 $T_{1\cdots r_1},T_{r_1+1\cdots r_2}\cdots T_{r_{p-1}+1\cdots m}$。

    hl,rh_{l,r} 为先前我们所计算的,一个 SS 中等同于 TlrT_{l\cdots r} 的子序列个数。

    对于选好了 pp 个不同位置上的 SS,我们在第一个选取的 SS 中取了 T1r1T_{1\cdots r_1},有 h1,r1h_{1,r_1} 种方案;在第二个选取的 SS 中取了 Tr1+1r2T_{r_1+1\cdots r_2}hr1+1,r2h_{r_1+1,r_2} 种方案……

    最终根据乘法原理,共有

    i=1phri1+1,ri\prod_{i=1}^{p} h_{r_{i-1}+1,r_i}

    种方案。特殊地,我们令其中 r0=0,rp=nr_0=0,r_p=n

    但是我们还没有确定 ppSS 的位置啊?

    kkSS 里面选 ppSS,显然这一步的方案数是 CkpC_k^p。递推求组合数明显会超时,我们采用它的公式法:

    $$C_n^m=\frac{n!}{m!(n-m)!}=\frac{n(n-1)(n-2)\cdots (n-m+1)}{m!} $$

    其中 x!x! 表示 xx 的阶乘。

    取模操作中怎么处理分母中的 m!m!

    加入乘法逆元后同余问题的各项性质不会被打破。
    通俗地说,我们把 1m!\frac{1}{m!} 当做 m!998244351m!^{998244351} 处理即可。

    综上,对于将 TT 分割为 pp 个子串 $T_{1\cdots r_1},T_{r_1+1\cdots r_2}\cdots T_{r_{p-1}+1\cdots m}$,有

    Ckp×i=1phri1+1,riC_k^p\times \prod_{i=1}^{p} h_{r_{i-1}+1,r_i}

    种方案。

    但是我们还没有确定分割方案?

    打住。

    看看数据范围:m10m\le 10
    于是搜索分割方案,累加答案即可。

    总复杂度

    动归求 hh 数组复杂度:O(nm3)O(nm^3)
    搜索累加答案复杂度:O(m!×m)O(m!\times m)
    总复杂度:O(nm3+m!×m)O(nm^3+m!\times m)
    mm 个组合数可以预处理得出,其中逆元可以打表,复杂度不计。

    显然跑不满,最慢测试点 5ms

    代码

    参考程序

    #include<bits/stdc++.h>
    using namespace std;
    const long long MOD = 998244353;
    int n,m,len,jd[11],inv[11] = {0,1,499122177,332748118,748683265,598946612,166374059,855638017,873463809,443664157,299473306};
    long long k,jc[11],c[11],ans,f[11][11],g[11];
    char s[5001],t[11];
    void sch(int w){ //搜索 T 的分割方案 
    	if(w == len + 1){
    		long long cnt = c[len];
    		for(int i = 1;i <= len;i ++) cnt = cnt * f[jd[i - 1] + 1][jd[i]] % MOD;
    		ans = (ans + cnt) % MOD;
    		return; 
    	}
    	if(w == len){
    		jd[w] = m,sch(w + 1);
    		return;
    	}
    	for(int i = jd[w - 1] + 1;i <= m - len + w;i ++) jd[w] = i,sch(w + 1);
    	return;
    }
    int main(){
    	scanf("%lld%s%s",&k,s + 1,t + 1);
    	n = strlen(s + 1),m = strlen(t + 1);
    	c[1] = k % MOD;
    	for(int i = 2;i <= m;i ++) c[i] = c[i - 1] * (k % MOD - i + 1) % MOD * inv[i] % MOD; //预处理组合数 
    	for(int l = 1;l <= m;l ++){
    		for(int r = l;r <= m;r ++){ //计算 T 的每个子串对应的 h 数组值,这里用 f 存储 
    			memset(g,0,sizeof(g));
    			g[0] = 1;
    			for(int i = 1;i <= n;i ++) for(int j = r;j >= l;j --) if(t[j] == s[i]) g[j - l + 1] = (g[j - l + 1] + g[j - l]) % MOD;
    			f[l][r] = g[r - l + 1];
    		}
    	}
    	for(len = 1;len <= m;len ++) sch(1);
    	printf("%lld",ans);
    	return 0;
    }
    

    总结:一道颇具思维趣味性的题目,做法本身没有蓝题难度但是思维可以有,显然仍具优化空间,本题解只提供了一个基础的做法权当抛砖引玉,欢迎大家改进本蒟蒻的做法!

    闲话:赛时 RP 爆表,第一次成绩这么好!

    • 1

    信息

    ID
    10457
    时间
    1000ms
    内存
    128MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者