牛客小白月赛51G计算题(哈希 + 二分)

牛客小白月赛51 G计算题
题意:长度为 n n n的字符串,删除任意一段后缀(可为空),然后必须修改一个字符,求会产生多少回文串.


思路:分奇偶长度讨论回文串的贡献:

回文串长度为奇
1.本身就是回文串,中心字母可任意修改ans += 26;
2.差一个字符成为回文串,ans += 2;
3.差很多字符
回文串长度为偶
1.本身就是回文串,只可修改为原字母,ans += 1;
2.差一个字符成为回文串, a b z c b a abzcba abzcba, ans += 2;
3.差很多字符

本身是回文串很好判断,哈希一下就可以,差一个字符成为回文串的话,我们可以二分最长的公共前缀 l e n len len,然后会把子串分为两段,继续判断即可。比如 a b c z b z d b a abczbzdba abczbzdba,中心点为 b b b,最长公共前缀 l e n = 2 : z b z len = 2:zbz len=2:zbz,然后继续二分判断 a b 和 b a ab 和 ba abba

#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<ctime>
#include<bitset>
#include<unordered_map>
#include<unordered_set>

using namespace std;

#define INF 0x3f3f3f3f3f3f3f3f3fll
#define inf 0x3f3f3f3f
#define lowbit(x) x & -x
#define sqr(x) ((x) * (x))
#define pb push_back
#define se second
#define fi first
#define endl '\n'
#define vi vector<int>
#define vl vector<long long>
#define vii vector<pair<int, int>>
// #define int long long

typedef unsigned long long ull;
typedef long long ll;
typedef pair<int, int> pii;
template <typename T> void inline re(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
} 
template <typename T> void inline pr(T x) {
    if (x < 0) { putchar('-'); pr(-x); return ; }
    if (x >= 10) pr(x / 10);
    putchar((x % 10) + '0');
}

const int M = 4100, N = 1e6 + 10, mod = 1e9 + 7;
inline ll ksm(ll a, ll b){
    ll ans = 1; 
    for(; b; b >>= 1, a = a * a % mod) if(b & 1) ans = ans * a % mod;
    return ans;
}
//----------------------------------------------------------------------------------------//

int n; 
char s[N];
ull p[N], hl[N], hr[N];

ull get_hash(ull h[], int l, int r){
    return h[r] - h[l - 1] * p[r - l + 1];
}

int query(int x, int y){
    int l = 1, r = x;
    int res = 0;
    while(l <= r){
        int mid = l + r >> 1;
        
        if(get_hash(hr, n - x + 1, n - x + mid) == get_hash(hl, y, y + mid - 1)) l = mid + 1, res = mid;
        else r = mid - 1;
    }
    return res;
}
signed main() {
#ifdef JANGYI
    freopen("input.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    scanf("%d%s", &n, s + 1);
    p[0] = 1;
    for(int i = 1; i <= n; i++){
        p[i] = p[i - 1] * 131;
        hl[i] = hl[i - 1] * 131 + (s[i] - 'a' + 1) ;
    }
    int k = 1;
    for(int i = n; i >= 1; i--){
        hr[k] = hr[k - 1] * 131 + (s[i] - 'a' + 1);
        k++;
    }
    // cout << get_hash(hl, 1, 1) << ' ' << get_hash(hr, 1, 1) << '\n';
    int ans = 0;
    for(int i = 1; i <= n; i++){
        if(i & 1){ //奇数长度
            int mid = i + 1 >> 1;
            int len = query(mid, mid);
            // cout << len << endl;
            if(len == mid) ans += 26;
            else{
                int now = query(mid - len - 1, mid + len + 1);
                // cout << now << endl;
                if(now == mid - len - 1) ans += 2;
            }
        }else{
            int mid1 = i >> 1;
            int mid2 = mid1 + 1;
            int len = query(mid1, mid2);
            // cout << len << endl;
            if(len == mid1) ans += 1;
            else{
                int len_ = query(mid1 - len - 1, mid2 + len + 1);
                if(len_ == mid1 - len - 1) ans += 2;
            }
        }
    }
    cout << ans;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值