HDU1159 Common Subsequence
题目描述:
就是求最长公共字串的长度,比如两个字符串abcd,abdef,那么它们的最长公共字串为abd,
长度为3。这个题目它不要求字串连续,其实连续也是一样的,两种dp而已。具体题目如下:
http://acm.hdu.edu.cn/showproblem.php?pid=1159
思路:
两个字符串s1,s2。我用dp[i][j]表示s1前i个字符与s2的前i个字符的最长公共字串长度。那么我
这个值与dp[i-1][j-1 ], dp[i-1][j], dp[i][j-1]的关系是怎样的呢?dp最重要的就是一个状态转移。
其实不一定是从上面这三个状态转化而来,而是我有这么一个想法,我去比较s1[i]与s2[j],如果
相等,那么dp[i][j]的长度就是在原来dp[i-1][j-1]的基础上+1,如果不等就是MAX(dp[i-1][j],
dp[i][j-1])。这个状态转移的正确性是要证明的,这里显然正确O(∩_∩)O~。现在我把一个问题
已经转化成了它的子问题,其实它是一种递归,只不过它把每次你要递归的值已经保存了下来,也
就是说,当你要dp[i-1][j-1],dp[i][j-1],dp[i-1][j]的值时,你不用再去递归求解了,这就是一种
用空间换时间的策略,还有如果当你发现你存的值有些是没用的,那么常常可以进行状态压缩,比
如,计算最长上升子序列时有两种方法,一种没有进行状态压缩。到最后,递归有一个出口,那么
dp也要有一个初始值,在这里就是0个字符时,长度为0。
dp就是去想一个状态转移,然后不断去验证,考验的是你的建模能力和分析能力。它解决的一般是
最优化问题,最大值与最小值等。
我觉得我们要培养计算机的思想,碰到一个问题要想能不能把它分解成几个子问题,这些子问题也
许是嵌套的,那么就是递归,如果是这个大问题的几个方面,那就把它分开解决,一个函数就实现
一个功能,不要都把它们揉在一起。还有,必须剔除“找”的观念,计算机只有遍历。
代码:
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 int dp[1001][1001]; 6 int main() 7 { 8 string s1, s2; 9 int i, j; 10 while(cin >> s1 >> s2) 11 { 12 int lenX = s1.length(); 13 int lenY = s2.length(); 14 for(i = 0; i < lenX; i++) 15 for(j = 0; j < lenY; j++) 16 { 17 if(s1[i] == s2[j]) 18 dp[i+1][j+1] = dp[i][j] + 1 ;//dp[0][j] and dp[i][0] have initialized by 0. 19 else dp[i+1][j+1] = dp[i+1][j] > dp[i][j+1] ? dp[i+1][j] : dp[i][j+1]; 20 } 21 cout << dp[lenX][lenY] << endl; 22 } 23 return 0; 24 }