回文子串
原题在这里:
概述题意,问给定字符串s中有多少个子串是回文串,不同下标构成的子串视为不同子串。
analyse:
1.暴力,数据范围只有1e3,substr+reverse暴力判断
code:
class Solution { bool check(string s) { string x = s; reverse(x.begin(), x.end()); return x == s; } public: int countSubstrings(string s) { /* analyse: 尝试翻译为KMP: 末位跳转次数+len? len<=1000 x 肯定是有数据结构的规律的,但是这里就写暴力了 */ int ans = 0; for (int i = 0; i < s.length(); ++i) for (int j = s.length() - i; j > 0; --j) ans += check(s.substr(i, j)); return ans; } };
2.中心拓展,但不是想当然式的两边+1+1那种,考虑中心的奇偶性,分为两种判断:
code:
class Solution { int ans = 0; int count(string s, int l, int r) { int num = 0; while (l >= 0 && r < s.length() && s[l] == s[r]) ++num, --l, ++r; return num; } public: int countSubstrings(string s) { for (int i = 0; i < s.length(); ++i) ans += count(s, i, i) + count(s, i, i + 1); return ans; } };
3.manacher,也属于是中心拓展,但是manacher会利用前面判断对后序判断进行优化,等于是中心拓展的优化。
关键点在于:对于manacher的中心数组p的使用,p[i]表示以i为中心的最长回文串半径,那么对于有多少个回文子串即为sum(p[i]/2)即可,因为p所处理的字符串为预处理过的字符串,所以单个字符构成的字符串并不计入在内。
code:
class Solution { string S; void Init(string s) { S = "~#"; for (char i : s) S += i, S += '#'; } int manacher(string s) { int mx = 0, id = 0, l = s.length(); int ans = 0; // mx最大右端点,id最长回文串中心点 vector<int> p(l); for (int i = 1; i < l; ++i) { if (mx > i) p[i] = min(mx - i, p[id - (i - id)]); else p[i] = 1; while (s[i - p[i]] == s[i + p[i]]) ++p[i]; if (p[i] + i > mx) { mx = p[i] + i; id = i; } ans += p[i] / 2; } return ans; } public: int countSubstrings(string s) { /* analyse: 翻译为manacher: p[i]表示以i为中心的最长回文串半径长度,那么最终ans=sum(p[i]/2) */ Init(s); return manacher(S); } };
【Over】

浙公网安备 33010602011771号