POJ 1934 Trip【最长公共子序列输出】
Description
Alice and Bob want to go on holiday. Each of them has planned a route, which is a list of cities to be visited in a given order. A route may contain a city more than once.
As they want to travel together, they have to agree on a common route. None wants to change the order of the cities on his or her route or add other cities. Therefore they have no choice but to remove some cities from the route. Of course the common route should be as long as possible.
There are exactly 26 cities in the region. Therefore they are encoded on the lists as lower case letters from 'a' to 'z'.
As they want to travel together, they have to agree on a common route. None wants to change the order of the cities on his or her route or add other cities. Therefore they have no choice but to remove some cities from the route. Of course the common route should be as long as possible.
There are exactly 26 cities in the region. Therefore they are encoded on the lists as lower case letters from 'a' to 'z'.
Input
The input consists of two lines; the first line is Alice's list, the second line is Bob's list.
Each list consists of 1 to 80 lower case letters with no spaces inbetween.
Each list consists of 1 to 80 lower case letters with no spaces inbetween.
Output
The output should contain all routes that meet the conditions described above, but no route should be listed more than once. Each route should be printed on a separate line. There is at least one such non-empty route, but never more than 1000 different ones. Output them in ascending order.
Sample Input
abcabcaa acbacba
Sample Output
ababa abaca abcba acaba acaca acbaa acbca
思路:看到一大牛的博客这样写道:
1)首先按照常规的方法求出最长公共子序列的长度
也就是用O(MN)的那个动态规划,结果放在二维数组dp里
dp[i][j] = { 字串a的1~i部分与字串b的1~j部分的最长公共子序列的长度 }
2)求辅助数组
last1[i][j] = { 到下标i为止,字符j在字串a中最后一次出现的下标 }
last2[i][j] = { 到下标i为止,字符j在字串b中最后一次出现的下标 }
3)枚举最长公共字串的每一个字符
从最后一个字符开始枚举
比如说现在枚举最后一个字符是'C'的情况。
那么 'CDCD' 与 'FUCKC' 这两个字串。
一共有 (0, 2) (0,
4) (2, 2) (2. 4) 这四种可能。
很明显前三个是可以舍弃的,因为第四个优于前三个,为后续的枚举提供了更大的空间。
last数组正好是用来做这个的。
4)排序输出
代码里用了stl的set。
注意,由于刚刚的枚举过程是针对每个字符,所以是不用判重的。
代码如下:
#include<stdio.h> #include<set> #include<string.h> #include<iostream> #include<algorithm> #include<string> using namespace std; int dp[105][105]; char ch1[105], ch2[105], temp[105]; int last1[105][27], last2[105][27]; set<string>SET; void find(int x, int y, int len) { int i, j; if(len<=0) { SET.insert(&temp[1]); return ; } if(x>0&&y>0) { for(i=0; i<26; i++) { int t1=last1[x][i]; int t2=last2[y][i]; if(dp[t1][t2]==len) { temp[len]='a'+i; find(t1-1, t2-1, len-1); } } } } int main() { int i, j, len1, len2; while(scanf("%s %s", &ch1[1], &ch2[1])!=EOF) { memset(last1, 0, sizeof(last1)); memset(last2, 0, sizeof(last2)); memset(dp, 0, sizeof(dp)); len1=strlen(&ch1[1]); len2=strlen(&ch2[1]); for(i=1; i<=len1; i++) for(j=1; j<=len2; j++) { if(ch1[i]==ch2[j]) dp[i][j]=dp[i-1][j-1]+1; else dp[i][j]=max(dp[i][j-1], dp[i-1][j]); } for(i=1; i<=len1; i++) for(j=0; j<26; j++) { if(ch1[i]=='a'+j) last1[i][j]=i; else last1[i][j]=last1[i-1][j]; } for(i=1; i<=len2; i++) for(j=0; j<26; j++) { if(ch2[i]=='a'+j) last2[i][j]=i; else last2[i][j]=last2[i-1][j]; } temp[dp[len1][len2]+1]='\0'; find(len1, len2, dp[len1][len2]); set<string>::iterator it; for(it=SET.begin();it!=SET.end();it++) { printf("%s\n",(*it).c_str()); } } return 0; }