如题地址:http://acm.nyist.net/JudgeOnline/problem.php?pid=5
Binary String Matching
-
描述
-
Given two strings A and B, whose alphabet consist only ‘0’ and ‘1’. Your task is only to tell how many times does A appear as a substring of B? For example, the text string B is ‘1001110110’ while the pattern string A is ‘11’, you should output 3, because the pattern A appeared at the posit
-
输入
- The first line consist only one integer N, indicates N cases follows. In each case, there are two lines, the first line gives the string A, length (A) <= 10, and the second line gives the string B, length (B) <= 1000. And it is guaranteed that B is always longer than A. 输出
- For each case, output a single line consist a single integer, tells how many times do B appears as a substring of A. 样例输入
-
3 11 1001110110 101 110010010010001 1010 110100010101011
样例输出
-
3 0 3
虽然这道题是英文,但是稍微懂点英语的,都没有问题而且实例很明显,何况是我一个没有过四级的人,都看懂了。
思路:
这道题,第一时间想到的是暴力解决,得到两个字符串,for循环,截取比较,得出结果----结果不出意料也超时了。这就是所谓的BF算法
然后很自然想到用KMP算法,接着复习KMP算法,也是个学习的过程。这次和以往的不同,这次通过理解后,按自己的思路编写出来,虽然路途很艰辛,但是痛苦经历,印象更深。
KMP的算法关键还是在得到next数组上。依靠前后缀的相同个数,来判定next里的值,字面上理解是相同个数+1.详情看《大话数据结构》
关键代码:
书上是0号位保存长度,1号为真实值,有出入,相应实现值也有出入,感觉对算法不清晰,一有出入就会实现不了,很锻炼人的
public int[] get_next(String a){
int [] next=new int[a.length()];
int i=0;
int j=-1; //i和j初始化要特别注意是一前一后。
next[i]= -1; //初始
while(i<a.length()-1){ //这里要长度-1,是因为前一个比较,得到后一个的,i始终代表后一个
if(j==-1||a.charAt(j)==a.charAt(i)){ //相同继续比较+1,不同就回溯,指导第一个的时候,即:重新比较
j++;
i++;
// if(a.charAt(i)!=a.charAt(j)) //这里是升级版:让回溯得更彻底,处理aaaaab的等情况
next[i]=j;
// else
// next[i]=next[j];
}
else{
j=next[j]; //进行回溯
}
}
return next;
}
加上实现函数:---思路很想get_next()函数,但是稍微有点不同,但是这个不同也是挺致命的,会实现不了,题目就是因为这样出现wrong answer.
public voidcompareStrKMP(String a,String b){
//得到next数组
int[] next=get_next(a);
int i=0;
intj=0;//这里i和j是相同的子串和大串比较,而不是自身,所以相同
while(i<b.length()){//对应比较,得出响应的结果,而get_next[]是得到后一个结果,所以是length而不是length-1
if(j==-1 ||a.charAt(j)==b.charAt(i)){//j==-1是根据next[]数组里的第一位对应的,重新开始往下一个
i++;
j++;
}
else
j=next[j];
if(j==a.length()){
break; //说明已经找到完全相同的子串了
}
}
}
最后附上源码:
import java.util.Scanner;
/*
* Binary String Matching
*/
public class Main {
public static void main(String[] args) {
Scanner cin=new Scanner(System.in);
Main main=new Main();
while(cin.hasNext()){
int n=cin.nextInt();
for(int i=0;i<n;i++){
String a=cin.next();
String b=cin.next();
main.compareStrKMP(a, b);
}
}
}
//bf算法,直接暴力比较,破解 ----超时。。
public void compareStrBF(String a,String b){
int count=0; //记录多少个子串相同
int leng=a.length();
for(int i=0;i<b.length()-leng;i++){ //这里注意要b.length()-leng。当后面都不够子串长,就不需要比较了
String strTemp=b.substring(i,i+leng);
//System.out.println("strTemp="+strTemp);
if(strTemp.equals(a)){
count++;
}
}
System.out.println(count);
}
//kmp算法 书上是0号为装长度,改变后,对应数据要-1; 结果:91 308
public void compareStrKMP(String a,String b){
//得到next数组
int[] next=get_next(a);
int i=0;
int j=0;
int count=0;
while(i<b.length()){ //对应比较,得出响应的结果,会比get_next()多一个范围,wrong answer就错在这里
if(j==-1||a.charAt(j)==b.charAt(i)){
i++;
j++;
}
else
j=next[j];
if(j==a.length()){
count++;
i=i-(next[j-1]+1); //i也要进行回溯,由题得子串包含相同的,回溯到相应第一次出现上。
j=0; //回到最初的状态
}
}
System.out.println(count);
}
public int[] get_next(String a){
int [] next=new int[a.length()];
int i=0;
int j=-1;
next[i]=-1; //初始化,对应书初始化为0,很重要
while(i<a.length()-1){ //String.charAt()是从0~~length()-1的,并且这里是前一个比较,得出后一个的结果
if(j==-1||a.charAt(j)==a.charAt(i)){ //从字面上理解是i-1到0中,从前或者从后比较最大有多少个相同,结果+1
j++; //而实际是用相对应的字符是否相同,相同则next=j(是先i++,j++后),不同回溯,因为再此处的基础上叠加--之前会相同的。
i++;
// if(a.charAt(i)!=a.charAt(j)) //判子串重复,不应该有这个--根据题意:next数值小了,上面i的回溯小了,会遗漏结果
next[i]=j;
// else
// next[i]=next[j];
}
else{
j=next[j]; //进行回溯
}
}
return next;
}
}