剑指 Offer II 020. 回文子字符串的个数
剑指 Offer II 020. 回文子字符串的个数
给定一个字符串 s
,请计算这个字符串中有多少个回文子字符串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
输入:s = "abc" 输出:3 解释:三个回文子串: "a", "b", "c"
示例 2:
输入:s = "aaa" 输出:6 解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
回文串没怎么做过,看到这个题是有点懵的,最后看题解,挨个把方法写了一遍
第一个就是暴力O(n*n*n)的算法
class Solution {
public:
/*方法一:暴力时间复杂度是O(n*n*n)*/ int countSubstrings(string s) { if (s.size() == 0) return 0; int count = 0; for (int i = 0; i < s.size(); i++) { for (int j = i; j < s.size(); j++) { if (Pan(i, j, s)) count++; } } return count; } bool Pan(int i,int j, string &s) { while (i < j) { if (s[i] != s[j]) break; i++; j--; } return i>=j; }
}
第二个用了一个dp数组,时间复杂度O(n*n),空间复杂度O(n*n)
/*方法二:dp,时间复杂度是O(n*n),空间复杂度O(n*n)*/ int countSubstrings(string s) { int n = s.size(); vectorint>> dp(n, vector<int>(n)); for (int i = 0; i < n; i++) { dp[i][i] = 1; } int count = n; for (int i = n-1; i >=0; i--) { for (int j = i+1; j < n; j++) { if (s[i] == s[j]) { if (i+1==j) { dp[i][j] = 1; count++; } else { if (dp[i+1][j-1]) { count++; dp[i][j] = 1; } } } } } return count; }
第三个方法是中心扩散,这个是最常用的一个方法,效率也比较高时间复杂度O(n*n),空间复杂度O(1)
/*方法三:中心扩散*/ int n; int countSubstrings(string s) { this->n = s.size(); int res = 0; for (int i = 0; i < n; i++) { int odd = get_max_len(s, i, i); int even = get_max_len(s, i, i + 1); res += odd; res += even; } return res; } int get_max_len(string & s, int l, int r) { int cnt = 0; while (0 <= l && r < n && s[l] == s[r]) { cnt++; l--; r++; } return cnt; }
第四个方法马拉车,这个算法最高效,时间O(n),空间O(1),但是比较难理解,查了很多资料,最后才理解了思想,旦角以后可能也不会去用
/*方法四:马拉车算法*/ int countSubstrings(string s) { //---------------------------- 马拉车算法 ---------------------------// string t = "#"; for (char c : s) { t += c; t += '#'; } int tn = t.size(); int res = 0; vector<int> arm_len; int center = -1; int maxR = -1; for (int i = 0; i < tn; i++) { int cur_arm_len = 0; if (i <= maxR) { int mirror_i = 2 * center - i; int cur_min_len = min(arm_len[mirror_i], maxR - i); cur_arm_len = center_spread_get_max_len(t, i - cur_min_len, i + cur_min_len); } else { cur_arm_len = center_spread_get_max_len(t, i, i); } arm_len.push_back(cur_arm_len); if (i + cur_arm_len > maxR) { maxR = i + cur_arm_len; center = i; } res += (cur_arm_len + 1) / 2; //本题需要的部分 } return res; } int center_spread_get_max_len(string & t, int l, int r) { int tn = t.size(); while (0 <= l && r < tn && t[l] == t[r]) { l--; r++; } return (r - l - 2) / 2; }