例1:
给定彼此独立的两棵树头节点分别为t1和t2,判断t1中是否有与t2树拓扑结构完全相同的子树。
解:
①普通解法:二叉树遍历+匹配问题
考察t1中以每个节点为头的子树是否与t2一致,如果N代表t1节点数,M代表t2节点数,则时间复杂度为O(N*M);
②最优解为二叉树序列化+KMP算法
a.将二叉树t1序列化为字符串str1,t2序列化为字符串str2;
b.用KMP算法判断str1中是否含有str2;
c.如果str1包含str2,说明t1某子树与t2一致;否则,说明t1中没有任何一个子树与t2一致;
时间复杂度可以做到O(M+N)。
代码:
public boolean chkIdentical(TreeNode t1, TreeNode t2) {
String str1 = serialByPre(t1);
String str2 = serialByPre(t2);
return getIndexOf(str1, str2) != -1;
}
private String serialByPre(TreeNode head) {
if (head == null) {
return "#!";
}
String res = head.val + "!";
res += serialByPre(head.left);
res += serialByPre(head.right);
System.out.println(res);
return res;
}
// KMP
private int getIndexOf(String str1, String str2) {
if (str1 == null || str2 == null || str2.length() < 1 || str1.length() < str2.length()) {
return -1;
}
char[] str11 = str1.toCharArray();
char[] str22 = str2.toCharArray();
int[] nextArr = getNextArray(str22);
int index = 0;
int mi = 0;
while (index < str11.length && mi < str22.length) {
if (str11[index] == str22[mi]) {
index++;
mi++;
} else if (nextArr[mi] == -1) {
index++;
} else {
mi = nextArr[mi];
}
}
return mi == str22.length ? index - mi : -1;
}
private int[] getNextArray(char[] str22) {
if (str22.length == -1) {
return new int[] { -1 };
}
int[] nextArr = new int[str22.length];
nextArr[0] = -1;
nextArr[1] = 0;
int pos = 2;
int cn = 0;
while (pos < nextArr.length) {
if (str22[pos - 1] == str22[cn]) {
nextArr[pos++] = ++cn;
} else if (cn > 0) {
cn = nextArr[cn];
} else {
nextArr[pos++] = 0;
}
}
return nextArr;
}
例2:
给定连个字符串str1和str2,如果str1和str2中出现的字符串一样且每种字符出现的次数也一样,那么str1与str2互为变形词,请实现函数判断两个字符串是否互为变形词。
举例:
str1=“123”,str2“231”。返回true;
str1=“123”,str2“1231”。返回false。
解:
使用哈希表做字符统计
比对哈希表1与哈希表2的记录是否一致,可以用固定长度的数组代替哈希表结构,时间复杂度为O(N),额外空间复杂度为O(N)。
代码:
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
int[] sArray = new int[26];
int[] tArray = new int[26];
for (int i = 0; i < s.length(); i++) {
sArray[s.charAt(i) - 97]++;
tArray[t.charAt(i) - 97]++;
}
for (int i = 0; i < 26; i++) {
if (sArray[i] != tArray[i]) {
return false;
}
}
return true;
}
例3
如果一个字符串str,把字符串str前面任意的部分挪到后面去形成的字符串叫做str的旋转词。
例如:
a=“cdab”,b=“abcd”。返回true。
a=“1ab2”,b=“ab12”。返回false。
a=“2ab1”,b=“ab12”。返回true。
解:最优解时间复杂度为O(N)
a.判断str1与str2是否长度相等
b.如果长度相等,生成str1+str1的大字符串
c.用KMP算法判断大字符串是否含有str2
举例说明:
str1=“1234”
str1+str1=“12341234”
如果str1长度为N,在str1+str1的字符串中,任意长度为一个长度为N的字串都是str1的旋转词。
代码:
public boolean chkRotation(String a, int lena, String b, int lenb) {
if (a == null || b == null || lena != lenb) {
return false;
}
String b2 = b + b;
return getIndexOf(b2, a) != -1;
}
// KMP
private int getIndexOf(String str1, String str2) {
if (str1 == null || str2 == null || str2.length() < 1 || str1.length() < str2.length()) {
return -1;
}
char[] str11 = str1.toCharArray();
char[] str22 = str2.toCharArray();
int[] nextArr = getNextArray(str22);
int index = 0;
int mi = 0;
while (index < str11.length && mi < str22.length) {
if (str11[index] == str22[mi]) {
index++;
mi++;
} else if (nextArr[mi] == -1) {
index++;
} else {
mi = nextArr[mi];
}
}
return mi == str22.length ? index - mi : -1;
}
private int[] getNextArray(char[] str22) {
if (str22.length == -1) {
return new int[] { -1 };
}
int[] nextArr = new int[str22.length];
nextArr[0] = -1;
nextArr[1] = 0;
int pos = 2;
int cn = 0;
while (pos < nextArr.length) {
if (str22[pos - 1] == str22[cn]) {
nextArr[pos++] = ++cn;
} else if (cn > 0) {
cn = nextArr[cn];
} else {
nextArr[pos++] = 0;
}
}
return nextArr;
}
例4:
给定一个字符串str,请在单词间做逆序调整。
举例:
“pig loves dog” 逆序成“dog loves pig”.
“I’m a student.”逆序成“student. a I’m”.
解:
a.实现将字符串局部所有字符串逆序的函数f:第一个字符与最后一个字符交换,第二个字符与倒数第二个字符交换,这样一直交换对应位置的字符,直到最中间的字符;
b.利用f将字符串所有的字符逆序;
c.找到逆序后的字符串中每一个单词的区域利用f将每一个单词的区域逆序。
代码:
public String reverseSentence(String A, int n) {
if (A == null || n == 0) {
return A;
}
char[] s = A.toCharArray();
rotateWord(s);
return String.valueOf(s);
}
private void rotateWord(char[] s) {
if (s == null || s.length == 0) {
return;
}
reverse(s, 0, s.length - 1);
int l = -1;
int r = -1;
for (int i = 0; i < s.length; i++) {
if (s[i] != ' ') {// 判断单词区域
l = i == 0 || s[i - 1] == ' ' ? i : 1;
r = i == s.length - 1 || s[i + 1] == ' ' ? i : r;
}
if (l != -1 && r != -1) {
reverse(s, l, r);
l = -1;
r = -1;
}
}
}
private void reverse(char[] s, int start, int end) {
char tmp = 0;
while (start < end) {
tmp = s[start];
s[start] = s[end];
s[end] = tmp;
start++;
end--;
}
}
例5:
给定一个字符串str,和一个整数i,len代表长度。i代表str中的位置,将str[0…i]移到右侧,将str[i+1…N-1]移到左侧。
举例:
str=“ABCDE”,i=2。将str调整为“DEABC”。
要求,时间复杂度为O(N),额外空间复杂度为O(1)。
解:
a.将str[0…i]部分的字符逆序
ABCDE–>CBADE;
b.将str[i+1~N-1]部分的字符逆序
CBADE–>CBAED;
c.将str整体的字符逆序
CBAED–>DEABC。
代码:
public String stringTranslation(String A, int len, int i) {
char[] s = A.toCharArray();
rotatel(s, i);
return String.valueOf(s);
}
private void rotatel(char[] s, int i) {
if (s == null || i <= 0 || i >= s.length) {
return;
}
reverse(s, 0, i - 1);
reverse(s, i, s.length - 1);
reverse(s, 0, s.length - 1);
}
private void reverse(char[] s, int start, int end) {
char tmp = 0;
while (start < end) {
tmp = s[start];
s[start] = s[end];
s[end] = tmp;
start++;
end--;
}
}
例6:
给定一个字符串的数组strs,请找到一种拼接顺序,使得所有字符串拼接起来组成的大字符串是所有可能性中字典顺序最小的,并返回这个大字符串。
举例:
str=[“abc”,“de”]。可以拼成"abcde",也可以拼成"deabc",但前者字典顺序更小,所以返回"abcde"。
strs=[“b”,“ba”]。可以拼成"bba",也可以拼成"bab",但后者字典顺序更小,所以返回"bab"。
解:最优解的时间复杂度O(N*logN),其实质是一种排序的实现
如果str1+str2<str2+str1,则str1放在前面,否则,str2放在前面。
代码:
public class MyComparator implements Comparator<String> {
public int compare(String a, String b) {
return (a + b).compareTo(b + a);
}
}
public String findSmallest(String[] strs, int n) {
if (strs == null || n == 0) {
return "";
}
Arrays.sort(strs, new MyComparator());
String res = "";
for (int i = 0; i < n; i++) {
res += strs[i];
}
return res;
}