題目:
用來求解字符串匹配問題,比如str1中是否包含str2,如果包含就返回str2在str1中開始的位置,不包含返回-1。
思路:
1.在這里我們首先要知道何為最大前綴,何為最大后綴,由下圖所示
image.png
在這里要用到next數組,何為next數組呢,next數組就是一個存儲i位置(i位置之前的)最大前綴和最大后綴匹配長度的數組,比如一個數組[a,b,c,a,b] next值[-1,0,0,0,1],在這里規定最大前綴不包括最后一個字符,最大后綴不包括第一個字符,具體看下圖實例講解以及代碼注釋
看毛片.png
當cn位置的值和i-1不相等時,cn=next[cn]的情況可由下圖看出
image.png
代碼:
public static int KMP(String s1,String s2){
if (s1==null||s2==null||s1.length()<1||s2.length()<1){
return -1;
}
char[] str1 = s1.toCharArray();
char[] str2 = s2.toCharArray();
int i1 = 0;
int i2 = 0;
int[] next = getNextArray(str2);
while (i1<str1.length&&i2<str2.length){
//如果字符串匹配,就繼續往下比較
if (str1[i1]==str2[i2]){
i1++;
i2++;
}else if (next[i2]==-1){
//可以看作str1和str2的第一個就不匹配
i1++;
}else {
//求解出next數組后,再比較就相當于把str2的最大前綴的下一個字符和第一個不匹配的字符比較
i2 = next[i2];
}
}
return i2==str2.length ? i1-i2 : -1;
}
public static int[] getNextArray(char[] str2){
if (str2.length==1){
return new int[]{-1};
}
int[] next = new int[str2.length];
//人為規定,next數組0為-1,1為0
next[0] = -1;
next[1] = 0;
int pos = 2;
int cn = 0;
while (pos<next.length){
if (str2[pos-1]==str2[cn]){
//當前位置的next的值為++cn
next[pos++] = ++cn;
}else if (cn>0){
//cn的值變成原cn的next數組的cn值
cn = next[cn];
}else {
//該位置next為0
next[pos++] =0;
}
}
return next;
}
擴展1:
問題:
給定一個字符串str1,只能往str1的后面添加字符變成str2。
要求1:str2必須包含兩個str1,兩個str1可以有重合,但是不能以同一個位置開頭。
要求2:str2盡量短最終返回str2
思路:
1、利用next數組,求出包含字符串str1最后一個字符的最大相同前綴后綴;
例如abracadabra,其最大的相同前后綴是abra,長度是4,
2、向str1尾部添加從最大相同前綴后開始到結尾的子串;
例如,abracadabra+cadabra,即從下標4開始的子串添加到str1末尾,即是所求的str2。
此次的next數組大小不再是str1的長度,而是長度+1,因為要包含最后一個字符。
代碼:
public static String answer(String str){
if (str==null||str.length()==0){
return "";
}
char[] c = str.toCharArray();
if (str.length()==1){
return str+str;
}
if (str.length()==2){
return c[0]==c[1 ]? (str+String.valueOf(c[0])) : (str+str);
}
int endNext = endNextLength(c);
return str+str.substring(endNext);
}
public static int endNextLength(char[] chars){
int[] next = new int[chars.length+1];
int pos = 2;
int cn = 0;
next[0] = -1;
next[1] = 0;
while (pos<next.length){
if (chars[pos-1]==chars[cn]){
next[pos++]= ++cn;
}else if (cn>0){
cn = next[cn];
}else {
next[pos++] = 0;
}
}
return next[next.length-1];
}
擴展2
問題:
給定兩個二叉樹T1和T2,返回T1的某個子樹結構是否與T2的結構相等。
思路:
1、將二叉樹結構匹配問題轉換成字符串匹配問題。
2、二叉樹轉換成字符串。將二叉樹每個節點的值后面都添加一個特殊符號作為劃定值邊界的符號,如“!”,空節點用另一個特殊符號表示,如“#”,兩個二叉樹就轉換為兩個字符串。
eg
3
/ \
2 1
轉換完成后就變成"3!2!#!#!1!#!#!"
代碼
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
public static boolean isSubtree(Node t1, Node t2) {
String t1Str = serialByPre(t1);
String t2Str = serialByPre(t2);
return getIndexOf(t1Str, t2Str) != -1;
}
//
public static String serialByPre(Node head) {
if (head == null) {
return "#_";
}
String res = head.value + "_";
res += serialByPre(head.left);
res += serialByPre(head.right);
return res;
}
// KMP
public static int getIndexOf(String s, String m) {
if (s == null || m == null || m.length() < 1 || s.length() < m.length()) {
return -1;
}
char[] ss = s.toCharArray();
char[] ms = m.toCharArray();
int[] nextArr = getNextArray(ms);
int index = 0;
int mi = 0;
while (index < ss.length && mi < ms.length) {
if (ss[index] == ms[mi]) {
index++;
mi++;
} else if (nextArr[mi] == -1) {
index++;
} else {
mi = nextArr[mi];
}
}
return mi == ms.length ? index - mi : -1;
}
public static int[] getNextArray(char[] ms) {
if (ms.length == 1) {
return new int[] { -1 };
}
int[] nextArr = new int[ms.length];
nextArr[0] = -1;
nextArr[1] = 0;
int pos = 2;
int cn = 0;
while (pos < nextArr.length) {
if (ms[pos - 1] == ms[cn]) {
nextArr[pos++] = ++cn;
} else if (cn > 0) {
cn = nextArr[cn];
} else {
nextArr[pos++] = 0;
}
}
return nextArr;
}
public static void main(String[] args) {
Node t1 = new Node(1);
t1.left = new Node(2);
t1.right = new Node(3);
t1.left.left = new Node(4);
t1.left.right = new Node(5);
t1.right.left = new Node(6);
t1.right.right = new Node(7);
t1.left.left.right = new Node(8);
t1.left.right.left = new Node(9);
Node t2 = new Node(2);
t2.left = new Node(4);
t2.left.right = new Node(8);
t2.right = new Node(5);
t2.right.left = new Node(9);
System.out.println(isSubtree(t1, t2));
}