一、C++中String类的用法总结
二、与字符串相关的典型问题
1、查找一棵二叉树是否是另一棵二叉树的子结构
①普通解法:递归进行比较,O(N*M)
②最优解:树序列化为字符串+KMP算法,O(N+M)
class IdenticalTree {
public:
// 先把树序列化成字符串,与常规的二叉树遍历不同,这里遇到空指针就填‘#’是为了使序列唯一化,不然光凭先序序列是无法唯一确定二叉树的结构的。
void preSerial(TreeNode* tree,string &s) {
if (tree == NULL) {
s += '#';
return;
}
s += tree->val;
preSerial(tree->left,s);
preSerial(tree->right,s);
}
// 获取模式串的next数组
int length = s.size();
vector<int> next(length);
next[0] = 0;
for (int i = 1,q = 0; i < length;i++) {
while (q > 0 && s[i] != s[q])
q = next[q-1];
if (s[i] == s[q])
q++;
next[i] = q;
}
return next;
}
// 用kmp算法进行匹配判断
bool kmp(string &s1,string &s2) {
int length1 = s1.size(),length2 = s2.size();
if (length1 < length2)
return false;
vector<int> next = getNext(s2);
for (int i = 0, q= 0; i < length1;i++) {
while (q > 0 && s1[i] != s2[q])
q = next[q-1];
if (s1[i] == s2[q])
q++;
if (q == length2)
return true;
}
return false;
}
bool chkIdentical(TreeNode* A, TreeNode* B) {
string str1,str2;
preSerial(A,str1);
preSerial(B,str2);
return kmp(str1,str2);
}
};
2、判断两个字符串是否互为变形词
变形词:A和B中出现的字符种类相同且每种字符出现的次数相同。
①利用hash表来进行词频统计;
②用固定长度的数组来实现hash表。
public:
bool chkTransform(string A, int lena, string B, int lenb) {
if (lena != lenb || lena < 0)
return false;
int hashTable[256] = {0};
for (int i = 0;i < lena;i++) {
hashTable[A[i]]++; // 此题无需处理碰撞问题,且字符的ASCII码范围是0~255,所以哈希函数直接用直接寻址法,
hashTable[B[i]]--; // hash表长度就是ASCII码的范围。
}
for (int j = 0;j < 256;j++)
if (hashTable[j] != 0)
return false;
return true;
}
};
3、判断是否互为旋转词
对于一个字符串A,将A的前面任意一部分挪到后边去形成的字符串称为A的旋转词。
最优解法:将A串追加到A串自己的后面,这样AA串里的子串包含了全部的旋转词,再用kmp算法判断B串是否能与AA串匹配,若能,则B、A互为旋转词,反之则不是。
class
Rotation
{
public
:
vector<
int
>
getNext(string s) {
int
length
= s.size();
vector<
int
>
next(length);
next[
0
]
=
0
;
for
(
int
i
=
1
,q
=
0
;i
< length;i++) {
while
(q
>
0
&&
s[i] != s[q])
q
= next[q-
1
];
if
(s[i]
== s[q])
q++;
next[i]
= q;
}
return
next;
}
bool
kmp(string strA,string strB) {
int
length
= strB.size();
vector<
int
>
next = getNext(strB);
for
(
int
i
=
0
,q
=
0
;i
<
2
*length;i++)
{
while
(q
>
0
&&
strA[i] != strB[q])
q
= next[q-
1
];
if
(strA[i]
== strB[q])
q++;
if
(q
== length)
return
true
;
}
return
false
;
}
bool
chkRotation(string A,
int
lena,
string B,
int
lenb)
{
if
(lena
!= lenb)
return
false
;
A
+= A;
return
kmp(A,B);
}
};
4、字符串分块逆序※※※
即,将"dog loves pig"逆序成"pig loves dog",以空格为分界,将字符串中的单词顺序逆序。
解法:先将整个字符串逆序,再遍历一遍字符串,找到空格就把当前的单词进行逆序。
class Reverse {
public:
void reverse(string &s,int start,int end) {
char temp;
for (int i = start,j = end;i < j;i++) {
temp = s[i];
s[i] = s[j];
s[j] = temp;
j--;
}
return;
}
string reverseSentence(string A, int n) {
reverse(A,0,n-1);
for (int i = 0,j = 1;j <= n;j++) {
if (A[j] == ' ' || j == n) {
reverse(A,i,j-1);
i = j + 1;
}
}
return A;
}
};
5、字符串移位
将字符串的前缀移动到字符串的后面,如将"ABCDE"移位变成"DEABC"。
解法:先将整个字符串逆序,再将原来的前缀部分(已移动到后部)逆序,最后将原来的后缀(已移动到前部)逆序。
class Translation {
public:
void reverse(string &s,int start,int end) {
char temp;
for (int i = start,j = end;i < j;i++) {
temp = s[i];
s[i] = s[j];
s[j] = temp;
j--;
}
return;
}
string stringTranslation(string A, int n, int len) {
reverse(A,0,n-1);
reverse(A,0,n-len-1);
reverse(A,n-len,n-1);
return A;
}
};
如 ["abc",
"de"]应该拼接为"abcde"。
class Parenthesis {
解法:
①实际上就是对字符串进行排序,与数值排序不同之处在于大小的比较,两个字符串按字典序来比较大小应该是将两个字符串的各个字符分别比较,哪一个首先遇到较大的字符谁就较大,反之则较小。
②由于排完序后还要使拼接后的大字符串字典序最小,所以不能按单个字符串的字典序大小来排序,而应该将两个字符串按两种不同的顺序拼接得到的两个字符串的字典序来排序。例如“ba”和“b”,应该要比较“bab”和“bba”的字典序,前者小所以“ba”较小。
③采用堆排序算法进行排序。
解法二:用栈来实现,遍历字符串,遇到'('则将其入栈,遇到')'时先判断栈是否空,若空则返回false,否则出栈,遍历结束栈空返回true,不空返回false。
class
Prior
{
public
:
int
compare(string
A,string B) {
/*
string
str1 = A + B,str2 = B + A;
int
length
= str1.size();
for
(
int
i
=
0
;i
< length;i++) {
if
(str1[i]
< str2[i])
return
-
1
;
if
(str1[i]
> str2[i])
return
1
;
}
return
0
;
*/
return (A+B > B+A)?1:0;
}
void
heapify(vector<string>
&strArray,
int
p,
int
length)
{
string
temp = strArray[p];
int
j
=
2
*p
+
1
;
while
(j
< length) {
if
(j+
1
<
length && compare(strArray[j+
1
],strArray[j])
==
1
)
j++;
if
(compare(strArray[j],temp)
== 0
)
break
;
strArray[p]
= strArray[j];
p
= j;
j
=
2
*p
+
1
;
}
strArray[p]
= temp;
return
;
}
string
findSmallest(vector<string> strs,
int
n)
{
string
temp;
for
(
int
p
= n/
2
-
1
;p
>=
0
;p--)
{
heapify(strs,p,n);
}
for
(
int
j
= n-
1
;j
>
0
;j--)
{
temp
= strs[
0
];
strs[
0
]
= strs[j];
strs[j]
= temp;
heapify(strs,
0
,j);
}
temp
= strs[
0
];
for
(
int
k
=
1
;k
< n;k++) {
temp
+= strs[k];
}
return
temp;
}
};
7、判断一个字符串是否是合法的括号串
合法的括号串首先不含有别的符号,其次左括号和右括号应该要配对出现,任何一种多都不合法,最后还要保证先左括号后右括号。
解法一:O(n)、O(1)。用一个数num做统计,遍历字符串,遇到‘(’则num加1,遇到‘)’则num减1,遍历期间若num<0则直接返回false,否则继续遍历,遍历结束若num!=0则返回false,否则返回true。
class
Parenthesis
{
public
:
bool
chkParenthesis(string A,
int
n)
{
int
num
=
0
;
for
(
int
i
=
0
;i
< n;i++) {
if
(num
<
0
)
return
false
;
switch
(A[i])
{
case
'('
:
num++;
break
;
case
')'
:
num--;
break
;
default
:
return
false
;
}
}
return
(num
==
0
)?
true
:
false
;
}
};
public:
stack<char> s;
bool chkParenthesis(string A, int n) {
for (int i = 0;i < n;i++) {
switch (A[i]) {
case '(':
s.push(A[i]);
break;
case ')':
if (s.empty())
return false;
s.pop();
break;
default:
return false;
}
}
return (s.empty())?true:false;
}
};
8、求一个字符串中无重复字符的子串的最大长度
解法:遍历字符串,以当前字符结束的无重复字符子串向左延伸最远能到达的位置,要么是当前字符上一次出现的位置,要么是以上一个字符结束的无重复字符子串能到达的位置。因此可以迭代地计算。但是需要一个hash表来存储当前字符上一次出现的位置,需要一个变量来存储以上一个字符结束的无重复字符子串向左能到达的位置。
class DistinctSubstring {
public:
int longestSubstring(string A, int n) {
int index[26];
memset(index,-1,sizeof(int)*26);
int lastIndex = 0;
int length = 0,maxLength = 0;
for (int i = 0;i < n;i++) {
int j = (int)(A[i]-'a');
if (index[j] == -1 || index[j] < lastIndex) {
length = i - lastIndex + 1;
}
else {
length = i - index[j];
lastIndex = index[j] + 1;
}
index[j] = i;
if (length > maxLength)
maxLength = length;
}
return maxLength;