字符串
首尾双指针交换值即可。
1 2 3 4 5 6 7 void reverseString (vector<char >& s) { int l = 0 , r = s.size () - 1 ; while (l < r) swap (s[l++], s[r--]); }
模拟这个过程,计数每2k个对前k个进行翻转。最后对剩余部分判断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 string reverseStr (string s, int k) { int cnt = 0 ; int l, r, len = (int ) s.length (); for (int i = 0 ; i < len; i++) { if (++cnt == 2 * k) { l = i - k * 2 + 1 , r = i - k; while (l < r) swap (s[l++], s[r--]); cnt = 0 ; } } l = len - cnt; r = cnt > k ? len - (cnt - k) - 1 : len - 1 ; while (l < r) swap (s[l++], s[r--]); return s; }
还有一个更简便的方法,直接每次循环都迭代器加上2k(一个周期),这样只用判断当先迭代器加上k是否大于字符串长度,如果大于,则说明剩余长度小于k,翻转剩余全部。小于则说明剩余部分大于k,翻转其中长度为k的部分。
1 2 3 4 5 6 7 8 9 10 11 12 string reverseStr_compact (string s, int k) { for (int i = 0 ; i < s.length (); i += 2 * k) { if (i + k > s.length ()) reverse (s.begin () + i, s.end ()); else reverse (s.begin () + i, s.begin () + i + k); } return s; }
常规
额外创建一个字符串用于存放替换后的结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 string replaceSpace (string s) { string res; for (char c: s) { if (c != ' ' ) res.push_back (c); else res += "%20" ; } return res; }
双指针
先遍历一次字符串,统计空格的数量。然后扩大字符串,从后向前填充字符串。(前向后涉及移动字符,时间复杂度为\(O(n^2)\) 。)
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 string replaceSpace (string s) { int spaceCnt = 0 ; for (char c: s) if (c == ' ' ) spaceCnt++; int cur = s.size () - 1 ; s.resize (s.size () + 2 * spaceCnt); int pre = s.size () - 1 ; for (; prev < cur; cur--, pre--) { if (s[cur] != ' ' ) s[pre] = s[cur]; else { s[pre--] = '0' ; s[pre--] = '2' ; s[pre] = '%' ; } } return s; }
使用一个额外空间
倒序读取字符串,找出每个单词,添加到res
中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 string reverseWords (string s) { string res; int l = s.length () - 1 , r = l; while (l >= 0 ) { while (l >= 0 && s[l] == ' ' ) l--; if (l < 0 ) break ; r = l; while (r >= 0 && s[r] != ' ' ) r--; res += s.substr (r + 1 , l - r); res += ' ' ; l = r; } res.erase (res.end () - 1 ); return res; }
双指针
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 37 38 void removeExtraSpace (string& s) { int slow = 0 ; for (int fast = 0 ; fast < s.length (); fast++) { if (s[fast] == ' ' ) continue ; if (slow != 0 ) s[slow++] = ' ' ; while (fast < s.length () && s[fast] != ' ' ) s[slow++] = s[fast++]; } s.resize (slow); }string reverseWords (string s) { removeExtraSpace (s); reverse (s.begin (), s.end ()); int slow = 0 ; for (int fast = 0 ; fast <= s.length (); fast++) { if (s[fast] != ' ' && fast != s.length ()) continue ; reverse (s.begin () + slow, s.begin () + fast); slow = fast + 1 ; } return s; }
双指针
暂存需要被移动到末尾的子字符串。然后移动全部字符串。最后覆写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 string reverseLeftWords (string s, int n) { int pre = 0 , cur = n; string tmp = s.substr (0 , n); while (cur < s.length ()) s[pre++] = s[cur++]; int i = 0 ; while (pre < s.length ()) s[pre++] = tmp[i++]; return s; }
局部翻转+整体翻转
1 2 3 4 5 6 7 8 string reverseLeftWords2 (string s, int n) { reverse (s.begin (), s.begin () + n); reverse (s.begin () + n, s.end ()); reverse (s.begin (), s.end ()); return s; }
暴力模拟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int strStr_brutal (string haystack, string needle) { int len = needle.length (); if (len > haystack.length ()) return -1 ; for (int i = 0 ; i < haystack.length () - len + 1 ; i++) { if (haystack.substr (i, len) == needle) return i; } return -1 ; }
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 int * getNext (const string& str) { int * next = new int [str.length ()]; int pre = -1 ; next[0 ] = pre; for (int suf = 1 ; suf < str.length (); suf++) { while (pre >= 0 && str[suf] != str[pre + 1 ]) pre = next[pre]; if (str[suf] == str[pre + 1 ]) pre++; next[suf] = pre; } return next; }int strStr_KMP (string haystack, string needle) { int * next = getNext (needle); for (int i = 0 , j = -1 ; i < haystack.length (); i++) { while (j >= 0 && haystack[i] != needle[j + 1 ]) j = next[j]; if (haystack[i] == needle[j + 1 ]) j++; if (j == needle.length () - 1 ) return i - needle.length () + 1 ; } return -1 ; }
KMP
使用KMP 算法,对字符串进行遍历。因为上一个子串和下一个子串相同的情况下,模式串指针pre
和字符串指针cur
会相距一个固定距离。所以最后判断开始到pre的距离能否被剩余距离整除就可以判断是否有重复子字符串了。
同时也需要排除pre
指针退回到最开始-1的情况(如果是全减一的next
数组的话)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 bool repeatedSubstringPattern (string s) { int pre = -1 ; int next[s.length ()]; next[0 ] = pre; for (int cur = 1 ; cur < s.length (); cur++) { while (pre >= 0 && s[pre + 1 ] != s[cur]) pre = next[pre]; if (s[pre + 1 ] == s[cur]) pre++; next[cur] = pre; } cout << pre << endl; if ((pre + 1 ) % (s.length () - pre - 1 ) == 0 && pre != -1 ) return true ; return false ; }
利用循环节
将两个 s 作为一个字符串,然后从第二个字符开始寻找 s。
find 函数返回的是找到的第一个字符串的起始位置。
如果 s 是重复字符串,则从第二个字符开始寻找,就破坏了第一个 s 的重复性,但是可以通过和第二个 s 的字符串构成新的 s。
因此返回的值应该是小于 s.size()
的。
abcabc
abcabcabcabc
bcabcabc abc
abcd
abcdabcd
bcdabcd
1 2 3 4 bool repeatedSubstringPattern (string s) { return (s+s).find (s, 1 ) < s.size (); }