一、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;