字符串 字符串,就是由字符连接而成的序列。常见的字符串问题包括字符串匹配问题、子串相关问题、前缀/后缀相关问题、回文串相关问题、子序列相关问题等。
反转字符串 题目链接:344. 反转字符串
1 2 3 4 5 6 7 8 9 10 11 class Solution {public : void reverseString (vector<char >& s) { for (int i=0 ,j=s.size ()-1 ;i<j;i++,j--) { char a=s[i]; s[i]=s[j]; s[j]=a; } } };
反转字符串 题目链接:541. 反转字符串 II
注意一下reverse函数的用法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution {public : string reverseStr (string s, int k) { for (int i = 0 ; i < s.size (); i += (2 * k)) { if (i + k <= s.size ()) { reverse (s.begin () i, s.begin () + i + k ); } else { reverse (s.begin () + i, s.end ()); } } return s; } };
替换数字 题目链接:替换数字(第八期模拟笔试)
题目思路:看似简单,实则不好操作,gpt给的这个思路很好啊,重新弄一个新字符串然后拼接。代码随想录的那个太麻烦了,直接pass掉了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <iostream> #include <cctype> using namespace std;string replaceDigitsWithNumber (const string& s) { string result; for (char c : s) { if (isdigit (c)) { result += "number" ; } else { result += c; } } return result; } int main () { string s; cin >> s; cout << replaceDigitsWithNumber (s) << endl; return 0 ; }
主要难点在于如何去掉空格,使用快慢指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Solution {public : void reverse (string& s, int start, int end) { for (int i = start, j = end; i < j; i++, j--) { swap (s[i], s[j]); } } void removeExtraSpaces (string& s) { int slow = 0 ; for (int i = 0 ; i < s.size (); ++i) { if (s[i] != ' ' ) { if (slow != 0 ) s[slow++] = ' ' ; while (i < s.size () && s[i] != ' ' ) { s[slow++] = s[i++]; } } } s.resize (slow); } string reverseWords (string s) { removeExtraSpaces (s); reverse (s, 0 , s.size () - 1 ); int start = 0 ; for (int i = 0 ; i <= s.size (); ++i) { if (i == s.size () || s[i] == ' ' ) { reverse (s, start, i - 1 ); start = i + 1 ; } } return s; } };
右旋字符串 题目链接:右旋字符串
题目思路:和前边有一题很类似,都是申请了一个额外的空间往上加,这题是先把后n个加上,再把前m-n个加上。
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <iostream> using namespace std;int main () { int n; string s,ans; cin>>n>>s; int m=s.size (); for (int i=(m-n);i<m;i++) ans+=s[i]; for (int i=0 ;i<(m-n);i++) ans+=s[i]; cout<<ans; return 0 ; }
代码随想录:它要求不申请额外的空间,这个就直接reverse两次,和上一题差不多,先全局再局部。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <iostream> #include <algorithm> using namespace std;int main () { int n; string s; cin >> n; cin >> s; int len = s.size (); reverse (s.begin (), s.end ()); reverse (s.begin (), s.begin () + n); reverse (s.begin () + n, s.end ()); cout << s << endl; }
找出字符串中第一个匹配项的下标 题目链接:28. 找出字符串中第一个匹配项的下标
暴力解法:思路正确,但是我写的代码写的太麻烦了,下边贴一份优雅的代码。时间复杂度O(m*n)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : int strStr (string haystack, string needle) { int s[26 ]={0 }; if (needle.size ()>haystack.size ()) return -1 ; for (int i:needle) s[i-'a' ]++; for (int i=0 ;i<haystack.size ();i++) { int t; if (s[haystack[i]-'a' ]==0 ) continue ; else { t=i; int a=t; for (int j=0 ;j<needle.size ();j++) if (haystack.size ()-a>=needle.size () && haystack[a+j]==needle[j]) t++; } if ((t-i)==needle.size ()) return i; } return -1 ; } };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : int strStr (string s, string p) { int n = s.size (), m = p.size (); for (int i = 0 ; i <= n - m; i++){ int j = i, k = 0 ; while (k < m and s[j] == p[k]){ j++; k++; } if (k == m) return i; } return -1 ; } };
KMP解法: 当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。
前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。代码随想录写的这个就很通俗易懂。
Nex数组构造:
初始化
处理前后缀不相同的情况
处理前后缀相同的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Solution {public : void getNext (int * next, const string& s) { int j = -1 ; next[0 ] = j; for (int i = 1 ; i < s.size (); i++) { while (j >= 0 && s[i] != s[j + 1 ]) { j = next[j]; } if (s[i] == s[j + 1 ]) { j++; } next[i] = j; } } int strStr (string haystack, string needle) { if (needle.size () == 0 ) { return 0 ; } vector<int > next (needle.size()) ; getNext (&next[0 ], needle); int j = -1 ; for (int i = 0 ; i < haystack.size (); i++) { while (j >= 0 && haystack[i] != needle[j + 1 ]) { j = next[j]; } if (haystack[i] == needle[j + 1 ]) { j++; } if (j == (needle.size () - 1 ) ) { return (i - needle.size () + 1 ); } } return -1 ; } };
重复的子字符串 题目链接:459. 重复的子字符串
暴力解法:想用暴力法写一下,结果写了两个小时,还是看了题解写出来的,钻牛角尖了。我写的代码性能不如官方代码高,贴一下官方代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution {public : bool repeatedSubstringPattern (string s) { int n = s.size (); for (int i = 1 ; i * 2 <= n; ++i) { if (n % i == 0 ) { bool match = true ; for (int j = i; j < n; ++j) { if (s[j] != s[j - i]) { match = false ; break ; } } if (match) { return true ; } } } return false ; } };
移动匹配:将两个 s 连在一起,并移除第一个和最后一个字符。如果 s 是该字符串的子串,那么 s 就满足题目要求。
1 2 3 4 5 6 7 8 9 class Solution {public : bool repeatedSubstringPattern (string s) { string t = s + s; t.erase (t.begin ()); t.erase (t.end () - 1 ); if (t.find (s) != std::string::npos) return true ; return false ; } };
KMP实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Solution {public : void getNext (int * next, const string& s) { next[0 ] = -1 ; int j = -1 ; for (int i = 1 ;i < s.size (); i++){ while (j >= 0 && s[i] != s[j + 1 ]) { j = next[j]; } if (s[i] == s[j + 1 ]) { j++; } next[i] = j; } } bool repeatedSubstringPattern (string s) { if (s.size () == 0 ) { return false ; } int next[s.size ()]; getNext (next, s); int len = s.size (); if (next[len - 1 ] != -1 && len % (len - (next[len - 1 ] + 1 )) == 0 ) { return true ; } return false ; } };