hiho一下 第六十周 String Matching Content Length
2015-08-27 16:29
295 查看
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
We define the matching contents in the strings of strA and strB as common substrings of the two strings. There are two additional restrictions on the common substrings.
The first restriction here is that every common substring's length should not be less than 3. For example:
strA: abcdefghijklmn
strB: ababceghjklmn
The matching contents in strA and strB are substrings ("abc", "jklmn"). Note that though "e" and "gh" are common substrings of strA and strB, they are not matching content because their lengths are less than 3.
The second restriction is that the start indexes of all common substrings should be monotone increasing. For example:
strA: aaabbbbccc
strB: aaacccbbbb
The matching contents in strA and strB are substrings ("aaa", "bbbb"). Note that though "ccc" is common substring of strA and strB and has length not less than 3, the start indexes of ("aaa", "bbbb", "ccc") in strB are (0, 6,
3), which is not monotone increasing.
Two lines. The first line is strA and the second line is strB. Both strA and strB are of length less than 2100.
The maximum length of matching contents (the sum of the lengths of the common substrings).
样例输入
样例输出
子序列:不连续的元素
比如
gtdzx0添加评论
满城风雨0gtdzx
4天前
dp[i][j][0]并不一定等于dp[i][j][1]。我看了你的程序,虽然最后结果对了,但是中间很多dp[i][j]的值是不正确的。也许你的dp[i][j]定义有些不同?
满城风雨
4天前
没有,其实DP[I][J][0]与DP[I][J][1]的定义是重合的
满城风雨
4天前
你定义DP[I][J][0]=MAX(DP[I-K][J-K][1]+K),K∈[3..f[i][j]],实际上DP[i][j][1]=MAX(DP[I][J][0]+1,DP[I-3][J-3][1]+3)=MAX(DP[I-K][J-K][1]+K),这两个绕了一圈又重合了
gtdzx
4天前
DP[I][J][0]并不是MAX(DP[I-K][J-K][1]+K),K∈[3..f[i][j]]。你可以试一下"baba"和"babacaba"这组数据,我的dp[4][8][0]是4,dp[4][8][1]是3。而你的程序dp[4][8]=3,我觉得如果你的dp定义和我一样的话,dp[4][8]的值是错的。
满城风雨
4天前
我的代码有一个地方有漏洞 if(f[i][j]==3)dp[i][j]=dp[i-3][j-3]+3; 这个地方出现了错误,应该是 if(f[i][j]==3)dp[i][j]=max(max(dp[i-1][j],dp[i][j-1]),dp[i-3][j-3]+3); 我一开始开文章的时候“其中dp[i][j][0]对应上文分析中的dp[i][j], dp[i][j][1]对应dp1[i][j]”这句话漏掉了,不过确实可以转化成二维,辅助数组是可以去掉的。
满城风雨
4天前
中间计算的转移值要一直与max(dp[i-1][j],dp[i][j-1])比较忘记了,谢谢哦。
添加评论
4o(n^3)次方的伪代码有问题,无论是否有f[i][j]>=3都应该更新dp[i][j]即:
题目1 :
时间限制:10000ms单点时限:1000ms
内存限制:256MB
描述
We define the matching contents in the strings of strA and strB as common substrings of the two strings. There are two additional restrictions on the common substrings.The first restriction here is that every common substring's length should not be less than 3. For example:
strA: abcdefghijklmn
strB: ababceghjklmn
The matching contents in strA and strB are substrings ("abc", "jklmn"). Note that though "e" and "gh" are common substrings of strA and strB, they are not matching content because their lengths are less than 3.
The second restriction is that the start indexes of all common substrings should be monotone increasing. For example:
strA: aaabbbbccc
strB: aaacccbbbb
The matching contents in strA and strB are substrings ("aaa", "bbbb"). Note that though "ccc" is common substring of strA and strB and has length not less than 3, the start indexes of ("aaa", "bbbb", "ccc") in strB are (0, 6,
3), which is not monotone increasing.
输入
Two lines. The first line is strA and the second line is strB. Both strA and strB are of length less than 2100.
输出
The maximum length of matching contents (the sum of the lengths of the common substrings).样例输入
abcdefghijklmn ababceghjklmn
样例输出
8
和题意分析
给定只包含字母的两个字符串A,B,求A,B两个字符串的最长公共子序列,要求构成子序列的子串长度都必须大于等于3。比如[code]"abcdefghijklmn"
"ababceghjklmn",其最长满足题意要求的子序列为
"abcjklmn",其由公共子串
"abc"和
"jklmn"组成。这里我们要注意
子串和
子序列的区别:子串:连续的元素
子序列:不连续的元素
比如
"abcdefghijklmn"和
"ababceghjklmn"的最长公共子串就只是
"jklmn"了。
算法分析
首先我们来复习一道经典的题目:给定只包含字母的两个字符串A,B,求A,B两个字符串的最长公共子序列。比如"abcde"和
"abdfg"的最长公共子序列为
"abd"对于最长公共子序列,我们知道解法为
dp[0][0..j] = 0 // 边界 dp[0..i][0] = 0 // 边界 For i = 1 .. n For j = 1 .. m If a[i] == b[j] Then dp[i][j] = dp[i - 1][j - 1] + 1 Else dp[i][j] = Max(dp[i - 1][j], dp[i][j - 1]) End If End For End For而这一道题目是在最长公共子序列上加入了一个条件:构成最长公共子序列的每一个子串长度必须大于等于3.一个简单的想法:我们求出最长公共子序列,然后将其中长度小于3的部分去掉。显然,这是不对的。举个例子:
"aaabaa"和
"acaaaca"的最长子序列为
"aaaaa"。其对应关系为:
[code]a aaba a acaa aca因为在
"acaaaca"中第一个字母a长度为1,所以我们需要去掉它,对应的我们也去掉了
"aaabaa"中第一个字母a。
[code]. aaba a . caa aca此时构成
"aaabaa"和
"acaaaca"公共子序列的3个子串为
"aa",
"a"和
"a",长度都小于了3,所以全部删去,则得到了新的公共子序列长度为0。这显然不正确,因为实际有符合题意要求的公共子序列:
[code] aaa baa ac aaa ca其中包含有长度为3的公共子序列。对最大公共子序列的结果进行再次处理这个方法不可行,那么我们只能从计算公共子序列的算法着手。首先我想我们可以做一个预处理,用
f[i][j]表示以a的第i个字母作为结尾的前缀和*以b的第j个字母作为结尾的前缀*的公共后缀的长度。这样看上去似乎很绕,不如举个例子:a=
"abcd"和b=
"acbc"。
f[3][4]的就表示a[1..3]和b[1..4]的公共后缀的长度,其中a[1..3]=
"abc",b[1..4]=
"acbc",其公共后缀为
"bc",所以f[3][4]=2.预处理的伪代码为:
[code]For i = 1 .. n For j = 1 .. m If a[i] == b[j] Then f[i][j] = f[i - 1][j - 1] + 1 Else f[i][j] = 0 End If End For End For有了这个预处理的数组,我们可以在原来最大公共子序列上做这样一个改进:
[code]dp[0][0..j] = 0 // 边界 dp[0..i][0] = 0 // 边界 For i = 1 .. n For j = 1 .. m If f[i][j] >= 3 Then // 改进 dp[i][j] = dp[i - f[i][j]][j - f[i][j]] + f[i][j] Else dp[i][j] = Max(dp[i - 1][j], dp[i][j - 1]) End If End For End For这个改进的意义为:当我们出现一个长度大于3的子串时,我们就直接将这个子串合并入我们的子序列。加入这个改进后,我们通过了样例的数据,这样看上去似乎就应该没什么问题了。然而事实并不是这样,在这道题目中还隐藏着陷阱:比如
"abcdef"和
"abcxcdef"根据我们算法,上面这个例子算出的结果为4,然而其实际的结果应该为6,即
"abc"和
"def"两个公共子串构成的子序列。那么出错的原因在哪?就在字符串
"cdef"上。我们计算结果出4是因为将
"cdef"看做了一个整体,而将
"abcdef"分割成了
"ab"和
"cdef"。在DP的过程中f[6][7] = 4,我们使用了dp[6][7] = dp[2][3] + 4,而dp[2][3] = 0,所以dp[6][7] = 4。
[code]ab cdef abcxcdef而实际上的最有解是将f[6][7]看作3,dp[6][7] = dp[3][4] + 3,其中dp[3][4] = 3,得到了dp[6][7] = 6。
[code]abc def abcxcdef也就是说,如果我们将f[i][j]>3的子串进行分割,有可能得到更优的情况。因此我们需要进一步的改进:
[code]dp[0][0..j] = 0 // 边界 dp[0..i][0] = 0 // 边界 For i = 1 .. n For j = 1 .. m dp[i][j] = 0 If f[i][j] >= 3 Then // 改进 For k = 3 .. f[i][j] // 枚举分割长度 dp[i][j] = Max(dp[i][j], dp[i - k][j - k] + k) End For Else dp[i][j] = Max(dp[i - 1][j], dp[i][j - 1]) End If End For End For但是这样的改进使得整个算法的时间复杂度变为了O(n^3),当n=2100时,有可能会超时。让我们考虑一下如何进一步改进这个算法。以上算法复杂度高的地方在于对于每一个(i, j),我们为了计算dp[i][j]都需要枚举分割长度k:
[code]For k = 3 .. f[i][j] // 枚举分割长度 dp[i][j] = Max(dp[i][j], dp[i - k][j - k] + k) End For这一步实际上我们计算了
max{dp[i-k][j-k]+k}, k=3..f[i][j]。我们不妨把它记作dp1[i][j],即:
[code]dp1[i][j] = max{dp[i-k][j-k]+k} = max{dp[i-3][j-3]+3, dp[i-4][j-4]+4, dp[i-5][j-5]+5, ... }同时
[code]dp1[i-1][j-1] = max{dp[i-1-3][j-1-3]+3, dp[i-1-4][j-1-4] + 4, dp[i-1-5][j-1-5]+5 ... } = max{dp[i-4][j-4]+3, dp[i-5][j-5]+4, dp[i-6][j-6]+5, ... }我们可以发现,dp1[i][j]的展开式中除了dp[i-3][j-3]+3这一项,是与dp1[i-1][j-1]中的每一项一一对应的,并且刚好大1。所以实际上dp[i-1][j-1]计算时枚举过分割长度,我们并不需要再次计算:
[code]dp1[i][j] = max{dp1[i-1][j-1] + 1, dp[i-3][j-3]+3}最后得到我们新的伪代码如下,其中dp[i][j][0]对应上文分析中的dp[i][j], dp[i][j][1]对应dp1[i][j]:
[code]dp[0][0..j][0..1] = 0 // 边界 dp[0..i][0][0..1] = 0 // 边界 For i = 1 .. n For j = 1 .. m dp[i][j][1] = 0 If f[i][j] >= 3 Then // 改进 dp[i][j][1] = Max(dp[i][j][1], dp[i - 3][j - 3][0] + 3) // 以长度3为分割 If (f[i][j] > 3) Then //按照dp[i-1][j-1][1]的分割方式分割,即直接将(i,j)接在(i-1,j-1)后面 dp[i][j][1] = Max(dp[i][j][1], dp[i - 1][j - 1][1] + 1) End If End If dp[i][j][0] = Max(dp[i-1][j][0], dp[i][j-1][0], dp[i][j][1]) End For End For至此我这道题目也算是完整的解出了。
结果分析
这个题目是在经典的动态规划题目《最长公共子序列》上做了一点修改。虽然只增加了一个条件,不过难度增大很多。能想出一个复杂度是O(n^2)的正确算法不是很容易,需要仔细分析清楚各种情况。一不小心就会掉进各种陷阱里。很多选手都能够想到经典最长子序列的改进算法而获得80分。剩下的测试点则对应了算法分析中提到的陷阱,所以能否找出这种特殊的例子也是解决这道题的关键。不过微软的出题人似乎没有想太为难大家,数据并不是很强。在实际的比赛中,O(n^3)的算法也能拿到满分,最终该题目的通过率为9%。很多O(N^2)的程序不能通过"babad"和
"babacabad"这组数据。提问于 4天前
gtdzx0添加评论
6个回答
0dp[i][j][0] = Max(dp[i-1][j][0], dp[i][j-1][0], dp[i][j][1]) 没有必要,dp[i][j][0] 一定等于dp[i][j][1],整个DP数组可就只需要两维就够了。4天前满城风雨0gtdzx
4天前
dp[i][j][0]并不一定等于dp[i][j][1]。我看了你的程序,虽然最后结果对了,但是中间很多dp[i][j]的值是不正确的。也许你的dp[i][j]定义有些不同?
满城风雨
4天前
没有,其实DP[I][J][0]与DP[I][J][1]的定义是重合的
满城风雨
4天前
你定义DP[I][J][0]=MAX(DP[I-K][J-K][1]+K),K∈[3..f[i][j]],实际上DP[i][j][1]=MAX(DP[I][J][0]+1,DP[I-3][J-3][1]+3)=MAX(DP[I-K][J-K][1]+K),这两个绕了一圈又重合了
gtdzx
4天前
DP[I][J][0]并不是MAX(DP[I-K][J-K][1]+K),K∈[3..f[i][j]]。你可以试一下"baba"和"babacaba"这组数据,我的dp[4][8][0]是4,dp[4][8][1]是3。而你的程序dp[4][8]=3,我觉得如果你的dp定义和我一样的话,dp[4][8]的值是错的。
满城风雨
4天前
我的代码有一个地方有漏洞 if(f[i][j]==3)dp[i][j]=dp[i-3][j-3]+3; 这个地方出现了错误,应该是 if(f[i][j]==3)dp[i][j]=max(max(dp[i-1][j],dp[i][j-1]),dp[i-3][j-3]+3); 我一开始开文章的时候“其中dp[i][j][0]对应上文分析中的dp[i][j], dp[i][j][1]对应dp1[i][j]”这句话漏掉了,不过确实可以转化成二维,辅助数组是可以去掉的。
满城风雨
4天前
中间计算的转移值要一直与max(dp[i-1][j],dp[i][j-1])比较忘记了,谢谢哦。
添加评论
4o(n^3)次方的伪代码有问题,无论是否有f[i][j]>=3都应该更新dp[i][j]即:
dp[i][j] = Max(dp[i-1][j],dp[i][j-1])伪代码应改为:
[code]dp[0][0..j] = 0 // 边界 dp[0..i][0] = 0 // 边界 For i = 1 .. n For j = 1 .. m dp[i][j] = 0 dp[i][j] = Max(dp[i - 1][j], dp[i][j - 1]) If f[i][j] >= 3 Then // 改进 For k = 3 .. f[i][j] // 枚举分割长度 dp[i][j] = Max(dp[i][j], dp[i - k][j - k] + k) End For End If End For End For
相关文章推荐
- hdu 1596 find the safest road
- 二级目录访问mvc项目
- C++中的static关键字
- Beyond Compare作为git的比对与合并工具
- Qt for Mac:发布程序(widgets和quick2)
- IntelliJ IDEA快捷键
- js高阶函数
- 饭卡
- 文件夹及文件操作
- C# 多文档界面 页面切换
- Sumo第一次使用博客
- 问题四20150827解决maven打包编译出现File encoding has not been set问题
- IOS常见错误分析解决(一直更新) 你值得收藏-综合贴
- android开启手机虚拟键的菜单键
- hpu 2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活 【多重背包模板】
- POJ 2513 Colored Sticks (Trie树,欧拉通路,并查集)
- 如何修改LR自带的示例程序端口号
- 聚集索引和非聚集索引(整理)
- VMware安装Redhat9
- nodejs模仿http请求组件nodegrass简单例子