Problem Description
You are given a number of case-sensitive strings of alphabetic characters, find the largest string X, such that either X, or its inverse can be found as a substring of any of the given strings.
Input
The first line of the input file contains a single integer t (1 <= t <= 10), the number of test cases, followed by the input data for each test case. The first line of each test case contains a single integer n (1 <= n <= 100), the number of given strings, followed by n lines, each representing one string of minimum length 1 and maximum length 100. There is no extra white space before and after a string.
Output
There should be one line per test case containing the length of the largest string found.
Sample Input
2
3
ABCD
BCDFF
BRCD
2
rose
orchid
Sample Output
2
2
在这总结一下n个字符串找公共子串的套路:
以最短的子串开始 .
1,先截取1个字符,然后到其余的n-1个字符去比较,发现区域的n-1个字符有包含这个字符的,则记录下来,为1个公共字符串,长度为1的子串。如题目中的ABDC,先截取A,然后跟后面2个字符串匹配,发现都没有A,则继续取B比较,发现都有B,则B保存下来。
2,在第一步的基础上往后再截取1个字符(如果第一步存在1个公共子串,则把第二部截取的字符拼接到第一步的子串后面),然后继续在n-1个字符串中比较,匹配成功了.取C加上第一步的B,构成BC。然后匹配其他2个字符串,发现第三个字符串没有BC,所以BC不可以,放弃,只能取B,公共子串长度仍为1。(注意,BC不可以,不代表C不可以)。
继续做第一步,发现C可以,再发现D也可以,所以更新为CD。
注意:
如果A没有,则以A为前缀的字符串都没有,所以就可以考虑把以A为开头的子串都放弃掉,转向比较B。
按照这个思路,自己写了一个java版的复杂度为 o(n)
package AA;
public class my
{
static String sArray[] = { "ABCD", "BCDFF","CDFFy" };
public static boolean isSubString(String s) {
boolean flag = true;
for (String a : sArray) {
if (a.indexOf(s) == -1 && a.indexOf(inverse(s)) == -1) {
flag = false;
break;
}
}
return flag;
}
public static String inverse(String s) {
String t = "";
for (char a : s.toCharArray())
t = a + t;
return t;
}
public static void main(String argc[])
{
String f="";
int i=0,j=1,length=0;
boolean flag=true;
String sub="";
while(true)
{
if(i>3)break;
flag=true;
String t = sArray[0];
String sub1 = t.substring(i,i+j);
sub = sub+sub1;
if( isSubString(sub)==false)
{
flag=false;
}
if(flag == true)
{
i++;
f=sub;
}
else if(flag ==false)
{
sub="";
i++;
}
}
System.out.println(f.length());
}
}
上面的代码从目标串开始检测,我把它称为 “ 头暴力“ ,缺点就是 必须把整个目标串遍历完才能保证找最长子串
package AA;
//公共子串问题,暴力破解注意剪枝
public class Test3_2 {
static String sArray[] = { "rose", "orchid" };
public static boolean isSubString(String s) {
boolean flag = true;
for (String a : sArray) {
if (a.indexOf(s) == -1 && a.indexOf(inverse(s)) == -1) {
flag = false;
break;
}
}
return flag;
}
public static String inverse(String s) {
String t = "";
for (char a : s.toCharArray())
t = a + t;
return t;
}
/**
* @param args
*/
public static void main(String[] args) {
int len=sArray[0].length();
String t=sArray[0];
int maxlen=0;
for(int k=len;k>=1;k--){ //k不断截取字符串 长度为 4 3 2 1 当4的时候 做1次 当3的时候 做2次 当2的时候做3次 当1的时候,做四次
//从长到断,如果碰到了,就不再继续,跟我的那个算法刚好是反过来
boolean f=true;
for(int i=0;i<=len-k;i++){ //i?
int j=i+k; //j?
System.out.println(t.substring(i,j));
if(isSubString(t.substring(i,j))&&j-1+1>maxlen){
maxlen=k;
f=false;
break;//退出内循环达到有效的剪枝效果
}
}
if(!f)break;//退出外循环达到有效的剪枝效果
}
System.out.println(maxlen);
}
}
动态规划:
以一个m【i】【j】来表示当前 第i个字符到第j个字符的匹配情况,如果第i个字符到第j个字符是匹配的,既第i个字符到第j个字符能在其他串中找到相同的子串,则把m【i】【j】设为true m【i】【j】为true的条件是 m【i】【j-1】==true && m【j】【j】==true && 第j个字符能匹配
package AA;
//公共子串问题,暴力破解注意剪枝
public class Test3_2 {
static String sArray[] = { "rose", "orchid" };
public static boolean isSubString(String s) {
boolean flag = true;
for (String a : sArray) {
if (a.indexOf(s) == -1 && a.indexOf(inverse(s)) == -1) {
flag = false;
break;
}
}
return flag;
}
public static String inverse(String s) {
String t = "";
for (char a : s.toCharArray())
t = a + t;
return t;
}
/**
* @param args
*/
public static void main(String[] args) {
int len=sArray[0].length();
String t=sArray[0];
int maxlen=0;
for(int k=len;k>=1;k--){ //k不断截取字符串 长度为 4 3 2 1 当4的时候 做1次 当3的时候 做2次 当2的时候做3次 当1的时候,做四次
//从长到断,如果碰到了,就不再继续,跟我的那个算法刚好是反过来
boolean f=true;
for(int i=0;i<=len-k;i++){ //i?
int j=i+k; //j?
System.out.println(t.substring(i,j));
if(isSubString(t.substring(i,j))&&j-1+1>maxlen){
maxlen=k;
f=false;
break;//退出内循环达到有效的剪枝效果
}
}
if(!f)break;//退出外循环达到有效的剪枝效果
}
System.out.println(maxlen);
}
}