lc2484
前缀、后缀数组 ++二维hash统计数字对的出现次数++,枚举字符串中间字符
累加前后缀相同数字对的乘积,得到长度为5的回文子序列总数
喵喵二维hash的转移设计 统计能和当前字符组成长度为5的回文的数字对数量:
长度为5的回文格式是 j k d k j , d 是当前遍历到的中间字符。
pre2jk 表示 当前字符左侧 出现的 j 后跟 k 的数字对数量。
suf2jk 表示 当前字符右侧 出现的 k 后跟 j 的数字对数量。
两者相乘:
左侧每一个 j k 都能和右侧每一个 k j 搭配,与中间的 d 组成一个 j k d k j 型回文,累加所有 j、k 组合的乘积就是总回文数。

class Solution {
const long MOD = 1e9 + 7;
public:
int countPalindromes(string s) {
int suf10{}, suf21010{}, pre10{}, pre21010{};
for (int i = s.length() - 1; i >= 0; --i) {
char d = si - '0';
++for (int j = 0; j < 10; ++j)
suf2dj += sufj;++
++++sufd;++
//喵喵二维hash的转移设计 预处理后缀
}
long ans = 0L;
for (char d : s) {
d -= '0';
--sufd;
for (int j = 0; j < 10; ++j)
suf2dj -= sufj; // 撤销
for (int j = 0; j < 10; ++j)
for (int k = 0; k < 10; ++k)
++ans += (long) pre2jk * suf2jk; // 前后缀个数的排列组合++
for (int j = 0; j < 10; ++j)
++pre2dj += prej;
++pred; //前缀++
}
return ans % MOD;
}
};
lc1930
unordered_map<char, pair<int, int>> hash;算起止
class Solution {
public:
int countPalindromicSubsequence(string s)
{
int n = s.size();
unordered_map<char, pair<int, int>> hash;
for (int i = 0; i < n; ++i)
{
if (!hash.count(si))
hashs\[i].first = i;
hashs\[i].second = i;
}
int ret = 0;
for (auto& ch, pos : hash) {
int first = pos.first;
int last = pos.second;
if (last - first < 2) continue; // 中间没有字符,无法形成长度为3的回文
unordered_set<char> st;
for (int i = first + 1; i < last; ++i)
st.insert(si);
++ret += st.size();++
}
return ret;
}
};
优化
一次遍历+维护前后缀+枚举中间+位运算
前缀后缀二进制标记字符存在性
枚举字符串中间字符作为回文中心
++hasmid |= pre & suf;++
累加得到长度为3的回文子序列总数
for (int mask : has)
ans += popcount((uint32_t) mask);
++算法就是 一次遍历 不断维护++
class Solution {
public:
int countPalindromicSubsequence(string s) {
int n = s.size();
// 统计 1,n-1 每个字母的个数
int suf_cnt26{};
int suf = 0;
for (int i = 1; i < n; i++) {
int ch = si - 'a';
suf_cntch++;
suf |= 1 << ch; // 把 ch 记录到二进制数 suf 中,表示后缀有 ch
}
int pre = 0;
int has26{}; // hasmid = 由 alpha 组成的二进制数
for (int i = 1; i < n - 1; i++) { // 枚举中间字母 mid
int mid = si - 'a';
suf_cntmid--;
// 撤销 mid 的计数,++suf_cnt 剩下的就是后缀 i+1,n-1 每个字母的个数++
if (suf_cntmid == 0)
// 后缀 i+1,n-1 不包含 mid
suf ^= 1 << mid;
// 从 suf 中去掉 mid
pre |= 1 << (si - 1 - 'a');
// 把 si-1 记录到二进制数 pre 中,表示前缀有 si-1
++hasmid |= pre & suf;++
// 计算 pre 和 suf 的交集,++|= 表示把交集中的字母加到 hasmid 中++
}
int ans = 0;
for (int mask : has) {
ans += popcount((uint32_t) mask);
//每个字符 has一个的记录表
// mask 中的每个 1 对应着一个 alpha
}
return ans;
}
};