【C++】ZZ1864- 解题精讲

【Horn Coding Studio】CPP编程专栏

题目

题目描述

小明刚学习完最长公共前缀(zhui),觉得很有趣。
前缀就是原字符串从左往右的一段连续子字符串。abc的前缀是a, ab, abc。
两个字符串的最长公共前缀就是两个字符串从左往右最长的一段相同的连续子字符串。abc和abd的最长公共前缀是ab。
给小明一个长度为nn的字符串,请求出字符串的每个后缀与其本身的最长公共前缀的长度之和。

输入

一行一个字符串S。 1=<|S|<=100000。

输出

一行一个整数表示答案。

样例输入 复制

aaa

样例输出 复制

6

提示

样例输入2
qqwqe
样例输出2
7

来源

一点即通

字符串的前缀:符号串左部的任意子串(或者说是字符串的任意首部)

字符串的后缀:符号串右部的任意子串(或者说是字符串的任意尾部)

了解完这两个概念后,这两道题使用hash做也就很简单了(怎么可能)

另外,我们还需要用一下二分的方法,毕竟他的数据帆帆,核心代码就像这样:

while(L <= R) {
            mid = (L + R) >> 1;
            if(get_hash(1, mid) == get_hash(i, i + mid - 1)) {
                res = mid;
                L = mid + 1;
            }else {
                R = mid - 1;
            }
        }

怎么样?是不是很似曾相识?没错,标准二分欸!

代码

ZZOJ    AC 

#include<bits/stdc++.h>
#define p  13131
using namespace std;

int n, has[100005], pn[100005]={1};
char s[100005];
int get_hash(int l, int r) {
    int ans = (has[r] - has[l-1]*pn[r-l+1]);
    return (ans);
}
int main() {
    for(int i = 1; i <= 100005; i++){
        pn[i] = pn[i-1] * p;
    } 
    scanf("%s", s + 1);
    n = strlen(s + 1);
    for(int i = 1; i <= n; i++) {
        has[i] = (has[i - 1] * p + s[i] - 'a' + 1);
    }
    long long ans = n;
    for(int i = 2; i <= n; i++) {
        int L = 1, R = n - i + 1, mid, res = 0;
        while(L <= R) {
            mid = (L + R) >> 1;
            if(get_hash(1, mid) == get_hash(i, i + mid - 1)) {
                res = mid;
                L = mid + 1;
            }else {
                R = mid - 1;
            }
        }
        ans += res;
    }
    printf("%lld", ans);
    return 0;
}

 

posted @ 2022-06-25 23:02  冯子坤  阅读(53)  评论(0编辑  收藏  举报