枚举每个元素作为回文子序列中间那个字符的所有合法情况。
对于每个位置,用一个数组来表示在它之前的元素中0~9各有多少个,用哈希表来存储这个位置之前的00~99这一百种状态,即能够作为子序列前两个字符的情况个数各有多少。处理完这个位置的前缀信息,同理,也要处理它的后缀信息。
枚举每个位置上的元素作为中间元素的答案,累加求和即可。
STL的map会超时,需要剪枝;用一个二维数组来保存00~99的状态个数则不需要剪枝。
class Solution {
public:
static const int mod=1e9+7;
int countPalindromes(string s) {
int n=s.size();
vector<unordered_map<int,int>> a(n),b(n);
vector<vector<int>> p(n,vector<int>(10,0));
auto t=p;
int m=0;
for(auto c:s) m=max(m,c-'0');
for(int i=0;i<n;i++){
if(i==0){
p[i][s[i]-'0']++;
continue;
}
for(int j=0;j<=m;j++){
p[i][j]+=p[i-1][j];
int k=j*10+(s[i]-'0');
a[i][k]=p[i][j];
}
p[i][s[i]-'0']++;
for(int x=0;x<=m;x++){
for(int y=0;y<=m;y++){
int z=x*10+y;
a[i][z]+=a[i-1][z];
}
}
}
for(int i=n-1;~i;i--){
if(i==n-1){
t[i][s[i]-'0']++;
continue;
}
for(int j=0;j<=m;j++){
t[i][j]+=t[i+1][j];
int k=j*10+(s[i]-'0');
b[i][k]=t[i][j];
}
t[i][s[i]-'0']++;
for(int x=0;x<=m;x++){
for(int y=0;y<=m;y++){
int z=x*10+y;
b[i][z]+=b[i+1][z];
}
}
}
int ans=0;
for(int i=2;i<n-2;i++){
for(int x=0;x<=m;x++){
for(int y=0;y<=m;y++){
int z=x*10+y;
ans=(1LL*ans+1LL*a[i-1][z]*b[i+1][z]%mod)%mod;
}
}
}
return ans;
}
};
时间复杂度:O(nT^2),n为字符串长度,T为字符集个数,这里为10
空间复杂度:O(T^2+nT)