news 2026/11/6 14:17:07

2026-10-06:统计从两个字符串形成目标字符串的不同方案数。用go语言,给定两个字符串 word1、word2,以及一个目标字符串 target。现在需要从 word1 和 word2 中挑选字

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
2026-10-06:统计从两个字符串形成目标字符串的不同方案数。用go语言,给定两个字符串 word1、word2,以及一个目标字符串 target。现在需要从 word1 和 word2 中挑选字

2026-10-06:统计从两个字符串形成目标字符串的不同方案数。用go语言,给定两个字符串 word1、word2,以及一个目标字符串 target。现在需要从 word1 和 word2 中挑选字符,按顺序组成 target。

要求如下:

target 中的每一个字符,都必须由 word1 或 word2 中某个相同的字符来提供。

如果在组成 target 时,某个字符取自 word1,那么所有取自 word1 的字符在原字符串中的位置必须依次向后,不能相同,也不能倒退。

如果在组成 target 时,某个字符取自 word2,那么所有取自 word2 的字符在原字符串中的位置也必须依次向后,不能相同,也不能倒退。

最终不能只使用其中一个字符串,word1 和 word2 都必须至少贡献一个字符。

两种选择方案是否不同,取决于 target 的每一个位置到底选了哪个字符串,以及选的是该字符串中的哪一个下标。只要有一个位置对应的来源字符串不同,或者下标不同,就算不同方案。

求所有满足条件的方案数量。由于结果可能很大,最后需要对 10 的 9 次方加 7 取余,并返回取余后的结果。

1 <= word1.length, word2.length, target.length <= 100。

word1、word2 和 target 仅由小写英文字母组成。

输入: word1 = “abc”, word2 = “bac”, target = “abc”。

输出: 5。

解释:

有 5 种形成 target 的方案:

word1[0] = ‘a’, word1[1] = ‘b’, word2[2] = ‘c’

word1[0] = ‘a’, word2[0] = ‘b’, word1[2] = ‘c’

word1[0] = ‘a’, word2[0] = ‘b’, word2[2] = ‘c’

word2[1] = ‘a’, word1[1] = ‘b’, word1[2] = ‘c’

word2[1] = ‘a’, word1[1] = ‘b’, word2[2] = ‘c’

所有方案都保持了每个字符串内部递增的下标顺序,并且从每个字符串中至少选择了一个字符。

题目来自力扣3981。#=

大体步骤如下:

1. 明确状态含义

定义三个维度来表示动态规划的状态:

  • 第一个维度i:表示当前已经匹配到目标字符串target的前i个字符。
  • 第二个维度j:表示当前允许使用word1的前j - 1个字符,也就是下标从0到j - 2的字符。j的取值范围是1到m1 + 1,其中m1是word1的长度。
  • 第三个维度k:表示当前允许使用word2的前k - 1个字符,也就是下标从0到k - 2的字符。k的取值范围是1到m2 + 1,其中m2是word2的长度。

因此,状态f[i][j][k]表示:在只允许使用word1的前j - 1个字符和word2的前k - 1个字符的条件下,能够形成target前i个字符的方案总数。


2. 初始化空目标字符串的情况

当i = 0时,target为空字符串,不需要选择任何字符。只要允许使用的字符范围不是完全为空(即j >= 1且k >= 1),就都有一种方案:什么都不选。

所以,对于所有j从1到m1 + 1,k从1到m2 + 1,都令f[0][j][k] = 1。


3. 逐字符处理目标字符串

外层循环依次处理target的每一个字符。假设当前处理的是target的第i个字符(从0开始计数),字符为ch。

接下来要计算f[i + 1][j + 1][k + 1],即匹配了前i + 1个目标字符,并且允许使用word1前j个字符(下标0到j - 1)和word2前k个字符(下标0到k - 1)的方案数。

这里j从0遍历到m1,k从某个下界开始遍历到m2。下界是max(0, i + 1 - j),因为总共可用的字符数是j + k,要匹配i + 1个目标字符,必须满足j + k >= i + 1。


4. 利用二维前缀和进行容斥

计算f[i + 1][j + 1][k + 1]时,先考虑不依赖新加入的word1第j个字符和word2第k个字符的情况。这可以通过二维前缀和的容斥得到:

  • f[i + 1][j][k + 1]:允许word1前j - 1个字符,word2前k个字符。
  • f[i + 1][j + 1][k]:允许word1前j个字符,word2前k - 1个字符。
  • f[i + 1][j][k]:允许word1前j - 1个字符,word2前k - 1个字符。

基础方案数为:

f[i + 1][j][k + 1] + f[i + 1][j + 1][k] - f[i + 1][j][k]

这部分涵盖了所有不使用新字符来匹配当前目标字符的方案。


5. 加上使用word1新字符匹配当前目标字符的方案

如果当前目标字符ch等于word1中下标为j - 1的字符(前提是j > 0),那么可以用这个字符来匹配ch。此时,之前匹配target前i个字符时,必须恰好使用了word2中下标为k - 1的字符。这种方案数等于:

f[i][j][k + 1] - f[i][j][k]

解释:f[i][j][k + 1]允许word1前j - 1个字符,word2前k个字符;f[i][j][k]允许word1前j - 1个字符,word2前k - 1个字符。两者之差正好是必须使用word2第k个字符的方案数。这样加上后,就补全了所有使用word1第j个字符匹配当前字符的方案。


6. 加上使用word2新字符匹配当前目标字符的方案

类似地,如果当前目标字符ch等于word2中下标为k - 1的字符(前提是k > 0),则可以用这个字符匹配ch。此时,之前匹配target前i个字符时,必须恰好使用了word1中下标为j - 1的字符。这种方案数等于:

f[i][j + 1][k] - f[i][j][k]

解释:f[i][j + 1][k]允许word1前j个字符,word2前k - 1个字符;f[i][j][k]允许word1前j - 1个字符,word2前k - 1个字符。两者之差正好是必须使用word1第j个字符的方案数。加上后,就补全了所有使用word2第k个字符匹配当前字符的方案。


7. 取模并完成状态转移

将上述所有部分相加,得到f[i + 1][j + 1][k + 1]的原始值,然后对10^9 + 7取模,保证结果在整数范围内。


8. 计算最终答案

当所有目标字符处理完毕后,f[n][m1 + 1][m2 + 1]表示使用word1全部字符和word2全部字符时,形成整个target的方案总数。但这个总数包含了只使用word1或只使用word2的情况。

题目要求两个字符串都必须至少贡献一个字符,所以需要排除:

  • 只使用word1的方案:对应word2允许使用前0个字符,即k = 1,所以是f[n][m1 + 1][1]。
  • 只使用word2的方案:对应word1允许使用前0个字符,即j = 1,所以是f[n][1][m2 + 1]。

因此最终答案:

ans = f[n][m1 + 1][m2 + 1] - f[n][m1 + 1][1] - f[n][1][m2 + 1]

由于取模可能导致负数,最后再对10^9 + 7取模并调整到非负范围。


复杂度分析

  • 时间复杂度:
    外层循环遍历target的每个字符,共n次;中层循环遍历word1的允许前缀长度,共m1 + 1次;内层循环遍历word2的允许前缀长度,虽然下界是max(0, i + 1 - j),但最坏情况下接近m2次。因此总操作次数为O(n * m1 * m2)。每次操作都是常数时间的加减和取模,所以总时间复杂度为O(n * m1 * m2)。

  • 额外空间复杂度:
    使用了一个三维数组f,大小为(n + 1) * (m1 + 2) * (m2 + 2),所以额外空间为O(n * m1 * m2)。没有使用其他大规模数据结构。

由于题目中n, m1, m2都不超过100,最大状态数约为100^3 = 1,000,000,时间和空间都可以接受。

Go完整代码如下:

packagemainimport("fmt")funcinterleaveCharacters(word1,word2,targetstring)int{constmod=1_000_000_007n,m1,m2:=len(target),len(word1),len(word2)f:=make([][][]int,n+1)fori:=rangef{f[i]=make([][]int,m1+2)forj:=rangef[i]{f[i][j]=make([]int,m2+2)}}forj:=1;j<m1+2;j++{fork:=1;k<m2+2;k++{f[0][j][k]=1}}fori,ch:=rangetarget{forj:=rangem1+1{// j+k >= i+1fork:=max(0,i+1-j);k<=m2;k++{res:=f[i+1][j][k+1]+f[i+1][j+1][k]-f[i+1][j][k]ifj>0&&word1[j-1]==byte(ch){res+=f[i][j][k+1]-f[i][j][k]}ifk>0&&word2[k-1]==byte(ch){res+=f[i][j+1][k]-f[i][j][k]}f[i+1][j+1][k+1]=res%mod}}}ans:=f[n][m1+1][m2+1]-f[n][m1+1][1]-f[n][1][m2+1]return(ans%mod+mod)%mod// 保证 ans 非负}funcmain(){word1:="abc"word2:="bac"target:="abc"result:=interleaveCharacters(word1,word2,target)fmt.Println(result)}

Python完整代码如下:

# -*-coding:utf-8-*-definterleave_characters(word1:str,word2:str,target:str)->int:MOD=1_000_000_007n,m1,m2=len(target),len(word1),len(word2)# f[i][j][k] 表示处理到 target 的前 i 个字符,# word1 中已用到前 j 个位置,word2 中已用到前 k 个位置的方案数f=[[[0]*(m2+2)for_inrange(m1+2)]for_inrange(n+1)]# 初始化:target 为空时,任意前缀状态都算 1 种方案forjinrange(1,m1+2):forkinrange(1,m2+2):f[0][j][k]=1fori,chinenumerate(target):forjinrange(m1+1):# k 的下界保证 j + k >= i + 1,即已选字符数足够覆盖 target 的前 i+1 个字符start_k=max(0,i+1-j)forkinrange(start_k,m2+1):res=f[i+1][j][k+1]+f[i+1][j+1][k]-f[i+1][j][k]ifj>0andword1[j-1]==ch:res+=f[i][j][k+1]-f[i][j][k]ifk>0andword2[k-1]==ch:res+=f[i][j+1][k]-f[i][j][k]f[i+1][j+1][k+1]=res%MOD ans=f[n][m1+1][m2+1]-f[n][m1+1][1]-f[n][1][m2+1]return(ans%MOD+MOD)%MODif__name__=="__main__":word1="abc"word2="bac"target="abc"result=interleave_characters(word1,word2,target)print(result)

C++完整代码如下:

#include<iostream>#include<vector>#include<string>#include<algorithm>usingnamespacestd;constintMOD=1000000007;intinterleaveCharacters(string word1,string word2,string target){intn=target.size();intm1=word1.size();intm2=word2.size();// f[i][j][k] 表示处理到 target 的前 i 个字符,// word1 已用到前 j 个位置,word2 已用到前 k 个位置的方案数vector<vector<vector<int>>>f(n+1,vector<vector<int>>(m1+2,vector<int>(m2+2,0)));// 初始化:target 为空时,任意前缀状态都算 1 种方案for(intj=1;j<m1+2;++j){for(intk=1;k<m2+2;++k){f[0][j][k]=1;}}for(inti=0;i<n;++i){charch=target[i];for(intj=0;j<=m1;++j){// 保证 j + k >= i + 1intstart_k=max(0,i+1-j);for(intk=start_k;k<=m2;++k){longlongres=f[i+1][j][k+1]+f[i+1][j+1][k]-f[i+1][j][k];if(j>0&&word1[j-1]==ch){res+=f[i][j][k+1]-f[i][j][k];}if(k>0&&word2[k-1]==ch){res+=f[i][j+1][k]-f[i][j][k];}f[i+1][j+1][k+1]=res%MOD;}}}longlongans=f[n][m1+1][m2+1]-f[n][m1+1][1]-f[n][1][m2+1];return(ans%MOD+MOD)%MOD;// 保证结果非负}intmain(){string word1="abc";string word2="bac";string target="abc";intresult=interleaveCharacters(word1,word2,target);cout<<result<<endl;return0;}

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/3 15:39:28

多容器共享目录权限踩坑记

背景项目中有算法服务和后端服务&#xff0c;后端服务负责文件上传与保存&#xff0c;算法服务负责文件内容解析、报告生成与保存&#xff1b;采用docker-compose方式部署&#xff0c; 两个服务都会读写同一目录 /shared&#xff08;挂载自宿主机 /data/project/share-data&…

作者头像 李华
网站建设 2026/11/3 5:08:33

DeepSeek LeetCode 237. 删除链表中的节点 Java实现

LeetCode 237. 删除链表中的节点 题目描述 有一个单链表的 head&#xff0c;我们想删除它其中的一个节点 node。 给你一个需要删除的节点 node。你将 无法访问 head。 链表的所有值都是 唯一的&#xff0c;并且保证给定的节点 node 不是链表中的最后一个节点。 删除给定的节点。…

作者头像 李华
网站建设 2026/11/4 9:23:20

【AI黑话日日新】Day 054|Synthetic Data(合成数据)

一句话说清:让 AI 模型自己“出题、写答案、编教材”来当别的模型的训练资料,而不是全靠从网上扒真实人类的文字。 1. 它到底在说什么 合成数据(Synthetic Data)指的是由程序或模型“造”出来的数据,而不是从真实世界里发生的事、测出来的数里直接采集的。放到大模型语境…

作者头像 李华
网站建设 2026/11/4 0:26:19

Linux CFS 调度器深度解析:调度时机、选任务与负载均衡

1. 从一个真实场景说起&#xff1a;为什么你的服务器会突然卡顿很多运维和开发同学都遇到过这种情况&#xff1a;一台跑着几十个服务的 Linux 机器&#xff0c;CPU 使用率看起来只有 40%&#xff0c;但业务响应时间却突然飙升&#xff0c;日志里出现大量超时。你登录上去用top一…

作者头像 李华
网站建设 2026/10/20 21:23:47

工程复盘|华丰国际皮革城隔墙项目(项城市)轻质水泥隔墙板商业综合体应用实践与工况分析

工程复盘&#xff1a;华丰国际皮革城隔墙项目装配式轻质水泥隔墙板应用实践 一、项目概况 项目名称&#xff1a;华丰国际皮革城隔墙项目 项目地址&#xff1a;河南省周口市项城市东方大道 130 号 实施单位&#xff1a;漯河市庆林建材商贸有限公司 应用产品&#xff1a;华豫林居…

作者头像 李华
网站建设 2026/11/5 23:25:11

Linux内核hibernation流程解析:从内存快照到ACPI S4恢复

这篇梳理一下Linux内核的hibernation流程&#xff0c;也就是常说的“挂起到磁盘”&#xff08;suspend-to-disk&#xff09;。很多人把resume、suspend、hibernate混着叫&#xff0c;实际在内核里这是完全不同的执行路径&#xff0c;涉及进程冻结、内存快照、swap写入、ACPI S4…

作者头像 李华