2192-Zipper 求最长公共子序列的解题报告
主要的思考方向就是求dp里面的最长公共子序列。至于字符串本身的判别并不是重点。
根据网上poj的分类,这个题是dp求最长公共子序列,其实用暴力的方法也能通过。暴力的方法比较简单就不多说了。主要讲讲dp吧。思路就是分别求第一个字符串和第二个字符串与第三个字符串的最长公共子序列的长度是否等于其自身的长度。lcs的算法比较简单,可参考算法导论直接写出。但是仅仅判断长度是不够的。还需要考虑两点:
(1)判断第三个字符串有无多余的字符。
(2)是否存在某个字符只出现一次却是第一、二个字符串公用的。
如果只是想ac掉这道题,考虑第一点就够了,因为本题数据太弱了,我就是只考虑了第一点,当水题做的。但是我想出题者的本意应该是第二点也需要考虑到的。因为我看到discuss里面有前辈这样说了。acm里面牛人可是非常多的。这两点可以通过两个双重循环一次解决,虽然是最耗时的,幸好数据规模较小,无大碍。设置一个标记数组标记状态即可。首先对第一个字符和第三个字符处理,如果某个字符都出现了,而且该下标处没有标记,则标记,break。对第二第三个字符串做同样处理。再看第三个字符串里面是否还有没有被标记的地方,有,则no,没有即yes。对于此类题目,细心是关键。此处提供代码,供参考:
题目出处:http://poj.org/problem?id=2192
#include<iostream> #include<fstream> #include<string> using namespace std; const int MAX = 401; int d[MAX][MAX], v[MAX]; int lcs(string s1, string s2)//LCS算法 { int k = (int)s1.length(); int l = (int)s2.length(); for (int i = 0; i <= k; i++) d[i][0] = 0; for (int i = 1; i <= l; i++) d[0][i] = 0; for (int i = 1; i <= k; i++) for (int j = 1; j <= l; j++) if (s1[i -1] == s2[j - 1]) d[i][j] = d[i - 1][j - 1] + 1; else d[i][j] = max(d[i - 1][j], d[i][j - 1]); return d[k][l]; } bool fun(string a, string b, string c)//判断c是否可以由a,b组成 { int la = (int)a.length(), lb = (int)b.length(), lc = (int)c.length(); if (la == lcs(a, c) && lb == lcs(b, c)) { int temp = 0, k = 0; memset(v, 0 ,sizeof(v)); for (int i = 0; i < la; i++) for (int j = 0; j < lc; j++) if (!v[j] && a[i] == c[j]) { v[j] = 1;break; } for (int i = 0; i < lb; i++) for (int j = 0; j < lc; j++) if (!v[j] && b[i] == c[j]) { v[j] = 1;break; } for (int i = 0; i < lc; i++) if (!v[i]) { temp = 1;break; } if (!temp)return true; } return false; } int main() { int tot, count = 1; string x, y, z; cin>>tot; while (tot--) { cin>>x>>y>>z; if (fun(x, y, z)) printf("Data set %d: yes\n", count++); else printf("Data set %d: no\n", count++); } return 0; }