Sdut 1008 最长公共子序列(n个字符串的最大公共字串)

Description

从一个给定的串中删去(不一定连续地删去)0个或0个以上的字符,剩下地字符按原来顺序组成的串。例如:“ ”,“a”,“xb”,“aaa”,“bbb”,“xabb”,“xaaabbb”都是串“xaaabbb”的子序列。(例子中的串不包含引号。)
 
编程求N个非空串的最长公共子序列的长度。限制:2<=N<=100;N个串中的字符只会是数字0,1,…,9或小写英文字母a,b,…,z;每个串非空且最多含100个字符;N个串的长度的乘积不会超过30000。

Input

文件第1行是一个整数T,表示测试数据的个数(1<=T<=10)。接下来有T组测试数据。各组测试数据的第1行是一个整数Ni,表示第i组数据中串的个数。各组测试数据的第2到N+1行中,每行一个串,串中不会有空格,但行首和行末可能有空格,这些空格当然不算作串的一部分。

Output

输出T行,每行一个数,第i行的数表示第i组测试数据中Ni个非空串的最长公共子序列的长度。

Sample

Input

1
3
ab
bc
cd

Output

0

解题思路:做过两个字符串的Lcs,这个题就是多维的Lcs问题,如何将多维转为一维是这个题的难点,在网上看了看,都是设置一个index用来标识多个字符串的不同长度形态,即每组不同长度的字符串都有一个唯一标志数字,这就将多维转化为一维,然后开始从后向前遍历,详细看代码:

package cn.wy.sdut.dp;

import java.util.Arrays;
import java.util.Scanner;

//这组肯定爆
//1234567890abcdefghijkhlmnopqrstuvwxyz09876543321xyzuvwopqrstabcdefghiljklmnopqrstuvwxyz
//09876543321xyzuvwopqrstabcdefghiljklmnopqrstuvwxyz1234567890abcdefghijkhlmnop
//12354567897dkfjlsdjlsjflsjdl
public class Main {
	
	static String[] s = new String[110];  //字符串数组
	static int[] dp = new int[300100]; // 状态数组, 很奇怪,c++不会爆数组,java不定个大点的就爆了
	static int[] len = new int[110];  // 字符串长度
	static int n;
	
	public static int lcs(int[] tem) {
		int index, i, j, ans;
		
		// 如果有字符串长度为0, 则返回0
		for(i=0; i<n; i++) {
			if(tem[i] == 0) return 0; 
		}
		
		// 建立映射
		for(index = tem[n-1] -1, i=n-1; i>=0; i--) {
			index = index*len[i] + tem[i] -1;
			System.out.println("index+"+i+":"+index);
		}
		
		//记忆化搜索
		if(dp[index] >= 0) return dp[index];
		
		//判断是否所有串最后的字符都想等
		for(i=1; i<n; i++) {
			if(s[i].charAt(tem[i] - 1) != s[0].charAt(tem[0] - 1))  break;
		}
		
		//如果最后的字符相同, 最大长度 +1,之后恢复字符串长度,方便下次遍历
		if(i >= n) {
			for(j=0; j<n; j++) {
				tem[j]--;
			}
			ans = lcs(tem) + 1;
			for(j=0; j<n; j++) {
				tem[j]++;
			}
		} else {  //否则,挨个枚举,求最大值
			ans = 0;
			for(j=0; j<n; j++) {
				tem[j]--;
				int t = lcs(tem);
				ans = Math.max(t, ans);
				tem[j]++;
			}
			
		}
		
		dp[index] = ans;
		return ans;
	}
	
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		
		int t = scan.nextInt();
		for(int t1=0; t1<t; t1++) {
			n = scan.nextInt();
			
			int[] tem = new int[110];   //临时数组,记录临时字符串长度
			for(int i=0; i<n; i++) {
				s[i] = new String();
				s[i] = scan.next();
				len[i] = tem[i] = s[i].length();
			}
			
			Arrays.fill(dp, -1);
			System.out.println(lcs(tem));
		}
		scan.close();
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值