发现字符串的匹配完全要考虑全面,如果考虑的情况不足够全面,就很可能出现这个例子可以运行,下一个例子的就行不通,毕竟匹配可能遇到各种各样的情况。本着可以实现效果就可以的原则,编的代码也实在是不优美,BM参考了别人的代码,因为写的精炼,按照自己的思路来写,然后发现有的可以运行,有的就达不到相应的效果。主要实现了暴力字符串匹配、KMP、BM、Sunday四种,几天的时间学习完的,回头再看的时候发现自己都有点忘记了,赶紧记下来~
暴力字符串匹配,效率比较低,因为对主串来说,模式串每次移动的位置都为一个单位。而其它的,KMP、BM、Sunday则是按照自己的原则尽可能的增大移动的位数。下面针对KMP、BM、Sunday简单的介绍
KMP
KMP主要是利用模式串本身的特点来计算出NEXT值,模式串中的每一个字符都有一个NEXT值,NEXT为整型数组,比如NEXT[i]就代表在模式串的第i个位置的部分匹配值,其实就是模式串和主串最后一个匹配位置的部分匹配值,i+1个位置两个串就不匹配了。利用已经匹配的长度减去部分匹配值就可以得到模式串的移动位数。其实大白话就是现在主串匹配的子串在模式串中是否还存在,在计算NEXT值时则是利用已经匹配模式串的前缀和后缀,求前缀和后缀的最大长度。NEXT值下标是从1开始的
文章http://blog.csdn.net/buaa_shang/article/details/9907183
public class KMP {
public static void main(String[] args) {
/*String str = "ababc";
String orig ="abababc";*/
String str = "abaabcac";
String orig ="abcabaaabaabcac";
/*String str = "aba";
String orig ="aba";*/
int[] next = get_Next(str);
for(int i=0;i<str.length()+1;i++){
System.out.print(next[i]);
}
System.out.println();
KMP.search(orig, str, next);
}
//next[j]表示在当前模式串到T[j]遇到失配的情况,在模式串中重新和主串匹配的位置
public static int[] get_Next(String str){
//对str求出各个字符位置的部分匹配值
int[] next = new int[str.length()+1];
int j=0;
next[0]=next[1]=0;
for(int i=1;i<str.length();i++){
while(j>0&&str.charAt(j)!=str.charAt(i))
j=next[j];
if(str.charAt(i)==str.charAt(j))j++;
next[i+1]=j;
}
return next;
}
//orig为主串,而find为模式串,查找匹配位置以及匹配长度
public static void search(String orig,String find,int[]next){
int j=0;
for(int i=0;i<orig.length();i++){
while(j>0&&orig.charAt(i)!=find.charAt(j))
j = next[j];
if(orig.charAt(i)==find.charAt(j)){
j++;
}
if(j==find.length()){
System.out.println("find at position " + (i - j+1));
System.out.println(orig.subSequence(i - j + 1, i + 1));
j = next[j];
}
}
}
BM,BM提出了坏字符和好后缀的概念,并提出了坏字符原则和好后缀原则,选择两种原则下移动位数最大的作为移动位数
文章http://blog.csdn.net/appleprince88/article/details/11881323
public class BM {
public static void main(String[] args) {
String str="exex";
String text ="exaxexex";
int[] suffix=new int[5];
BM.suffix(str, 4, suffix);
for(int i=0;i<suffix.length;i++){
System.out.print(suffix[i]);
}
int[] bmgs = new int[5];
BM.preBmGs(str, 4, bmgs);
BM.boyerMoore(str , text);
}
//会用到两个数组,一个是依照坏字符规则得到的应该的移位数
//另一个则是依照好后缀规则得到的应该的移位数
//map中存放的是模式串中的各个字符,以及字符在模式串中倒叙的索引
private static void preBmBc(String pattern,Map<String,Integer>bmbc){
for(int i=pattern.length()-2;i>=0;i--){
if(!bmbc.containsKey(pattern.charAt(i))){
bmbc.put(pattern.charAt(i)+"", pattern.length()-i-1);
}
}
}
//如果存在则返回值,如果不存在则返回模式串的长度,不是应该返回-1么
private static int getBmBc(String c,Map<String,Integer>bmbc,int m){
if(bmbc.containsKey(c)){
return bmbc.get(c);
}
else
return m;
}
public static void suffix(String pattern,int length,int[]suffix){
suffix[length-1]=length;
int q;
for(int i=length-2;i>=0;i--){
q = i;
while(q>=0&&pattern.charAt(q)==pattern.charAt(length-1-i+q))
{
q--;
}
suffix[i]=i-q;
}
}
public static void preBmGs(String pattern,int length,int[] bmgs){
//主要从字符串的三个方面来进行
//没有子串匹配上好后缀,也找不到一个后缀 EXAMPLE
// ######MPLE
//没有子串匹配上好后缀,单可以找到一个最大后缀 EXAMPLE
// #PLE###MPLE
//模式串中有子串匹配上好后缀 EXAMPLE
// #MPLE###MPLE
//先去处理一下模式串的后缀匹配情况
int[] suffix= new int[length];
int i,j;
suffix(pattern,length,suffix);
for(i=0;i<length;i++){
bmgs[i] = length;
}
j=0;
for(i=length-1;i>=0;i--){
if(suffix[i]==i+1){
for(;j<length-1-i;j++){
if(bmgs[j]==length){
bmgs[j]=length-i-1;
}
}
}
}
for(i=0;i<length-1;i++)
{
bmgs[length-1-suffix[i]]=length-1-i;
}
System.out.print("bmgs:");
for(int k=0;k<length;k++){
System.out.print(bmgs[k]);
}
System.out.println();
}
public static void boyerMoore(String pattern,String text){
int m = pattern.length();
int n = text.length();
Map<String,Integer> bmBc= new HashMap<String,Integer>();
int[] bmGs = new int[m];
preBmBc(pattern,bmBc);
preBmGs(pattern,m,bmGs);
int i=0;
int j=0;
int count=0;
while(j<=n-m)
{
for(i=m-1;i>=0&&pattern.charAt(i)==text.charAt(i+j);i--)
{ //用于计数
count++;
}
if(i<0){
System.out.println("one position is:"+j);
j+=bmGs[0];
}else{
j+=Math.max(bmGs[i],getBmBc(String.valueOf(text.charAt(i+j)),bmBc,m)-m+1+i);
}
}
System.out.println("count:"+count);
}
}
Sunday,从左向右进行比较,移动的位数则是从模式串相对于主串位置的下一个位置的字符是否能在模式串中找到,如果能找到,则秉着移动模式串使得模式串的相同的字符与主串该字符对齐的原则进行移动,否则则跳过,也就是说模式串的首字符要移动到该字符的下一个位置。
public class Sunday {
/**
* @param args
*/
public static void main(String[] args) {
/*String pattern = "bfde";
String text ="abcdebfdem";*/
/*String pattern ="mck";
String text = "abcdemcklm";*/
String pattern ="del";
String text = "decmendels";
System.out.print("模式串在主串的起始索引为:"+Sunday.sunday(pattern, text));
}
private static int getLocation(String pattern,String text,int length){
//判断在text中的length位置的字符是否在pattern中存在,如果存在,则返回,找不到
//则返回-1;
int i=pattern.length()-1;
while(i>=0&&pattern.charAt(i)!=text.charAt(length))
i--;
return i;
}
public static int sunday(String pattern,String text){
int i=0;
int j=0;
int dcount=0;//用count来记录从左向右匹配的字符数
int location =0;
boolean flag=false;
while(i<text.length()&&!flag){
j=0;
dcount =0;
flag = false;
System.out.print(i);
while(!flag&&j<pattern.length()&&i<text.length()&&text.charAt(i)==pattern.charAt(j)){
dcount++;
if(dcount==pattern.length()){
flag=true;
System.out.print("i"+i);
break;
}
i++;
j++;
}
if(j<pattern.length()&&i<text.length()&&text.charAt(i)!=pattern.charAt(j)){
location = getLocation(pattern,text,pattern.length()+i-dcount);
if(location!=-1)
{i =i+pattern.length()-dcount-location;}
else{
i=i+pattern.length()+1-dcount;
}
}
}
return i-pattern.length()+1;
}
}
暴力字符串匹配
public class Match1 {
/**
* @param args
*/
public static void main(String[] args) {
String string = "abcdefg";
String str = "cde";
System.out.print(MessageFormat.format("模式串在主串第{0}位置开始匹配,结束匹配位置为{1}", Match1.stringMatch(string, str),Match1.stringMatch(string, str)+str.length()-1));
}
public static int stringMatch(String string,String str){
//string 为主串,str为模式串
boolean flag = false;
int index =0;
/*System.out.print(string.length());*/
for(int i=0;i<string.length();i++){
index=i;
flag = true;
for(int j=0;j<str.length()&&flag;j++){
if(string.charAt(i)!=str.charAt(j)){
flag = false;
}else{
i++;
flag = true;
}
}
if(flag){
break;
}
}
return index;
}
}
字符串的匹配各种计算长度,计算位置,计算是否匹配,有的时候还要一直循环循环,设置一个标识符有时候还是蛮必要的。