题目来源链接
链接:https://ac.nowcoder.com/acm/contest/85187/E
来源:牛客网
题目
题目描述
定义一个字符串的“伪回文值”是:修改最少字符数量使得其变成回文串的修改次数。例如,"abca"的伪回文值是1。任何回文串的伪回文值是0。
现在小红拿到了一个字符串,她希望你求出所有连续子串的伪回文值之和。你能帮帮她吗?
输入描述
一个仅包含小写字母的字符串。长度不超过200000
输出描述
所有连续子串的回文值之和。
示例1
输入
abca
输出
6
示例2
输入
abac
输出
5
代码思路
字符串
a
1
a
2
a
3
.
.
.
a
n
−
1
a
n
a_1a_2a_3...a_{n-1}a_n
a1a2a3...an−1an
从字符串中部开始向外遍历,如果在某一层遍历时,所遍历到字符为
a
2
和
a
n
−
1
a_2和a_{n-1}
a2和an−1。
假设能求得以
a
2
a_2
a2为左边界的字符串
a
2
.
.
.
.
a
i
a_2....a_i
a2....ai 2<i<n-1 ,以
a
n
−
1
a_{n-1}
an−1为右边界的字符串
a
j
.
.
.
.
a
n
−
1
a_j....a_{n-1}
aj....an−1 2<=j<n-1 的伪回文值之和。
以abcdef为例
第一层遍历到c d,求出子串c,cd的伪回文值。
第二层遍历b e,求子串 bc,bcd,bcde,cde,de的伪回文值。
第三层遍历a f,求子串ab,abc,abcd,abcde,abcdef,bcdef,cdef,def,ef的伪回文值。
每一层结果相加即为最终答案。
如果能够根据上一层的结果求得下一层两个字母
a
1
a_1
a1为左边界的字符串
a
1
.
.
.
.
a
i
a_1....a_i
a1....ai 1<i<n ,以
a
n
a_{n}
an为右边界的字符串
a
j
.
.
.
.
a
n
−
1
a_j....a_{n-1}
aj....an−1 1<=j<n 的伪回文值之和。
,那么便能够在O(n)复杂度求出以任意
a
i
ai
ai为边界的子串的伪回文值之和,即我们要求的答案。
这一层需要求的子串有
a
1
.
.
.
a
i
a_1...a_i
a1...ai 1<i<n,
a
j
.
.
.
a
n
a_j...a_n
aj...an 1<=j<n。
对于
a
1
.
.
.
a
i
a_1...a_i
a1...ai 1<i<n, 可知,
a
1
.
.
.
a
i
a_1...a_i
a1...ai的内层为
a
2
.
.
.
a
i
a_2...a_i
a2...ai 2<i<n-1 刚好为上层求得结果,因此对于此字符串,只需判断边界是否需要修改if(a[1]==a[i]),再将结果相加即可。
例如上例中第三次 ab,abc,abcd,abcde内层为b(b的伪回文为0),bc,bcd,刚好为上层求过的,因此只需a与{b,c,d,e}比较是否需要修改即可。
通过遍历i进行判断无疑会减慢速度,因此通过cnt数组记录已遍历数字 a 2 . . . a i a_2...a_i a2...ai 2<i<n-1中a[1]的次数,已遍历数-a[1]次数+上层 a 2 . . . . a i a_2....a_i a2....ai 2<i<n-1 伪回文值之和即为 a 1 . . . . a i a_1....a_i a1....ai 1<i<n伪回文值之和, a j . . . a n a_j...a_n aj...an同理。
代码
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
void solve(string s)
{
int n=s.size();
vector<int> a(n),cnt(n);
for(int i=0;i<=n-1;i++){
a[i]=s[i]-'a';
}//转为数字存储
long long ans=0,sum=0;//ans存储最终结果
//num存储每层的结果 例如:abcde 遍历第二层 求以b或d为边界的子串的结果
//遍历第三次,求以a或e为边界的子串的结果,如此从最内侧循环,便得出以每个节点为边界的字串结果,相加即为ans
int tot = 0;
for(int i=(n-1)/2;i>=0;i--)
{
sum += tot - cnt[a[i]] ++ ; //遍历的总数tot-a[i]出现的次数 即是a[i]为边界,tot个已遍历的数字为另一边界
tot ++ ;//遍历数字+1
if(i!=n-i-1) //如果为奇数串,第一层遍历i=n-i-1,此数字对称位置还是自己 直接跳过即可
{
sum += tot - cnt[a[n-1-i]] ++ ;
tot ++ ;
}
ans += sum;//每次循环将结果加到最终结果
}
cout << ans << endl;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
string s;
cin>>s;
solve(s);
return 0;
}