牛客周赛 Round 48 E-小红的伪回文子串(hard)

题目来源链接

链接: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...an1an
从字符串中部开始向外遍历,如果在某一层遍历时,所遍历到字符为 a 2 和 a n − 1 a_2和a_{n-1} a2an1
假设能求得以 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} an1为右边界的字符串 a j . . . . a n − 1 a_j....a_{n-1} aj....an1 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....an1 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;
}


  • 33
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值