一、 前言
- 时间复杂度O(n)
- 解决包含问题
举例:判断str2是否包含于str1
str1:abc123def
str2:123d
解决方法 (str1长度为n, str2长度为m):
- 笨办法: 两个for循环遍历来匹配,复杂度O(n*m)
- KMP方法: 利用next数组j减少匹配次数
int getIndexOf(str1,str2) 就是KMP实现的
二、代码实现
KMP步骤一:先把str2计算出最长前缀和最长后缀相等的next数组
public static int[] getNextArray(char[] str2){
if(str2.length == 1){
return new int[]{-1};//如果只有一个,没有最长前缀,即-1
}
int[] next = new int[str2.length];
next[0] = -1;
next[1] = 0; //前两位默认
int i = 2;
int cn = 0; //标记回退的位置
while(i < next.length){
if(str2[i-1]==(str2[cn])){
next[i++] = ++cn;
}else if(cn > 0){
cn = next[cn]; //回退到上一个位置,继续判断是否有最长
}else{
next[i++] = 0; //没有最长前缀等于最长后缀的情况
}
}
return next;
}
KMP算法实现getIndexOf
public static int getIndexOf(String s, String m){
if(s==null || m==null || m.length() < 1 || s.length() < m.length()){
return -1;
}
char[] str1 = s.toCharArray();
char[] str2 = m.toCharArray();
int i1 = 0; //str1的下标
int i2 = 0; //str2的下标
int[] next = getNextArray(str2);
while(i1 < str1.length && i2 < str2.length){
if(str1[i1] == str2[i2]){
i1++;
i2++;
}else if (next[i2] == -1){ //第一个位置都没匹配,此时str2不动
i1++;
}else{
i2 = next[i2]; //st2可以往前跳
}
}
return i2 == str2.length? i1 - i2:-1;
}
三、 题目类型
3.1 字符串追加
举例:原始串abcabc,只能在后面添加字符得到字符串(包含里两个原始串,最长前缀和最长后缀等于原字符串),如何处理的大串最短。
答案:abcabcabc。
解题思路:就是求next数组,然后再求一个终止位置的next,,然后往前的字符串逆序补到字符串尾部。
Java 代码实现:
public static int[] getNextArray(char[] str){
if(str.length == 1){
return new int[]{-1};
}
int[] next = new int[str.length];
next[0] = -1;
next[1] = 0;
int i = 2;
int cn = 0;
while(i<str.length){
if(str[i-1]==str[cn]){
next[i++] = ++cn;
} else if(cn > 0){
cn = next[cn];
} else {
next[i++] = 0;
}
}
return next;
}
public static String process(char[] str){
int[] next = getNextArray(str);
int location = -1;
int cn = next[str.length-1];
while(location == -1){
if(str[str.length-1] == str[cn]){
location = next[str.length-1] + 1;
} else if (cn > 0){
cn = next[cn];
} else {
location = 0;
}
}
String result= "";
int resultLength = 2*next.length - location;
for (int i = 0; i < resultLength; i++) {
if(i < next.length){
result += str[i];
} else {
result += str[location];
location ++;
}
}
return result;
}
public static void main(String[] args) {
String str = "abcabc";
String result = process(str.toCharArray());
System.out.println(result);
}
3.2 子树问题
题目:两棵树T1和T2,可以值相同,可以值不同。T1中的某一个子树是否等于T2。
解题思路:T1的序列化和T2的序列化。如果S2是S1的子串,那么T2就是T1的子树
Java 代码实现:
public static boolean getIndexOf(String str1, String str2){
if(str1==null || str2==null || str2.length()<0||str2.length()>str1.length()){
return false;
}
String[] arr1 = str1.split("_");
String[] arr2 = str2.split("_");
int[] next = getNextArray(arr2);
int i = 0;
int j = 0;
while(i < arr1.length && j < arr2.length){
if(arr1[i].equals(arr2[j])){
i++;
j++;
}else if(next[j] == -1){
i++;
}else{
j = next[j];
}
}
return j == arr2.length; //str全部被匹配,返回true
}
public static int[] getNextArray(String[] arr){
int[] next = new int[arr.length];
next[0] = -1;
next[1] = 0;
int i = 2;
int cn = 0;
while(i < arr.length){
if(arr[i-1].equals(arr[cn])){
next[i++] = ++cn;
}else if(cn > 0){
cn = next[cn];
}else{
next[i++] = 0;
}
}
return next;
}
public static String serialTree(Node node){
if(node==null){
return "_"; //与通常的序列化不同,不需要占位
}
String res = node.value + "_";
res += serialTree(node.lChild);
res += serialTree(node.rChild);
return res;
}
public static void main(String[] args) {
Tree tree1 = new Tree(new Node(8));
tree1.add(9);
tree1.add(2);
Tree tree2 = new Tree(new Node(8));
tree2.add(8);
tree2.add(7);
tree2.add(9);
tree2.add(2);
tree2.add(4);
System.out.println("".length());
System.out.println(HasSubtree(tree2.root,tree1.root));
}
序列化的时候要改变:不能把null用占位符,否则结果会受到null的影响
举例:T1[8,8,7,9,2,#,#,#,#,4,7] T2[8,9,2,#,#,#,#\]
先序遍历方式序列化:T1[889##2477##] T2[892##2##\]
由于节点2(T1)下挂着节点47, 节点2(T2)挂着nullnull,导致序列化结果不匹配。
若不是节点2下,而是节点9下挂着47节点,会导致T1[889472##7##] T2[892##2##],在匹配前半段就不匹配了。
3.3 字符串重复判断
题目:怎么确定一个字符串是不是另一个字符串重复得到的,是返回true,不是返回false
即:str = str’*n
如:123123123123