• FJUT3703 这还是一道数论题(二分 + hash + manacher 或者 STL + hash 或者 后缀数组 + hash)题解


    Problem Description

    最后来个字符串签个到吧,这题其实并不难,所需的算法比较基础,甚至你们最近还上过课。

    为了降低难度,免得所有人爆零。这里给几个提示的关键字 :字符串,回文,二分,哈希. 注意要对奇偶回文分开二分

    这样还不会做,说明基础有所欠缺。

    给你一个字符串A和一个字符串B,请你求一个满足以下要求的所有字符串中,最长的字符串C的长度:

    1. C必须同时是A和B的子串,即A和B中都必须存在一个子区间和C长得一样

    2. C必须是一个回文,即正过来读和反过来读都一样

    Input

    多组数据,请处理到EOF

    每组数据包含,两行,每行都是仅由小写字符构成的字符串,代表A和B。

    对于30%的数据。

    保证|A|,|B|<=1000,且单个文件的字符总数小于10000

    对于100%的数据

    保证|A|,|B|<=100000,且单个文件的字符总数小于2e6

    其中70%的数据答案为奇数哦

    因为没有处理掉字符串尾巴上多余的' ',所以为了防止读到' ' 推荐使用scanf("%s");

    链接

    思路:

     更新2.0:

    旺神nb。发现A了之后就能看所有人代码了

    终于找到Hash的O(n)找公共子串的方法了,直接用unordered_map储存所有满足长度的回文子串。unordered_map查询接近常数级,就是O(1),插入反正比map快,所以总体就是O(n)级。

    然后就从7000ms瞬间降到1400ms

    代码2.0我没有预处理成全奇回文,而是直接分类奇偶二分。显然我们可以优化一下,奇偶遍历存在与否时可以直接两步两步走,因为奇偶的回文中心刚好交错。然后我们假如先二分奇数,得到一个长度R,那么我二分偶数回文半径至少R / 2 + 1,否则没有意义。我们直接用unordered_map储存s串的回文答案,然后O(n)判断p中有没有,如果怕Hash冲突,可以再验算一边是否一样。

    然后我发现时限变成4000ms了...7s的代码卡掉了...只能用2.0的代码过...

    这道题后缀数组也能做,JQtxdy + 

    ----------------代码1.0分割线(已T)----------------------------

    按照旺神的思路来。

    我是把串先按照Manacher处理成只有奇数回文,然后找到最大回文串R,显然最终答案只可能是R,R-2,R-4....那么我直接二分这个最终长度。然后用hash找是否存在这个长度的公共子串,但是我只会Hash的O(nlogn + m)写法啊。在T了几发之后发现题目时限又开大了,8000ms用7400ms擦过,旺神nb。

    代码2.0:

    #include<cmath>
    #include<set>
    #include<queue>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include <iostream>
    #include<algorithm>
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const int maxn = 200000 + 10;
    const ull seed = 131;
    const int INF = 0x3f3f3f3f;
    const int MOD = 1000000007;
    char s[maxn], p[maxn], snew1[maxn];
    int pos[maxn], num[maxn];
    ull hs[maxn], hp[maxn], fac[maxn], q[maxn];
    inline ull getHashP(int l, int r){
        return hp[r] - (l == 0? 0 : hp[l - 1]) * fac[r - l + 1];
    }
    inline ull getHashS(int l, int r){
        return hs[r] - (l == 0? 0 : hs[l - 1]) * fac[r - l + 1];
    }
    int lens, lenp, lenSnew;
    int init(int len){
        int cnt = 0;
        snew1[cnt] = '$';
        for(int i = 0; i < len; i++){
            snew1[++cnt] = '#';
            snew1[++cnt] = s[i];
        }
        snew1[++cnt] = '#';
        snew1[++cnt] = '';
        return cnt;
    }
    int Manacher(){
        int cnt = init(lens);
        lenSnew = cnt;
        int id = 0, ans = -1;
        for(int i = 2; i < cnt; i++){
            if(pos[id] + id > i){
                pos[i] = min(pos[2 * id - i], pos[id] + id - i);
            }
            else pos[i] = 1;
            while(snew1[i - pos[i]] == snew1[i + pos[i]])
                pos[i]++;
            if(id + pos[id] < i + pos[i])
                id = i;
            ans = max(ans,pos[i] - 1);
        }
        return ans; //长度
    }
    bool mid(int l, int r, ull aim){
        while(l <= r){
            int m = (l + r) >> 1;
            if(q[m] >= aim){
                if(q[m] == aim) return true;
                r = m - 1;
            }
            else l = m + 1;
    
        }
        return false;
    }
    unordered_map<ull, int> mp;
    //$#a#a#a#0
    bool checkJi(int len){
        mp.clear();
        for(int i = 2; i + len - 1 < lenSnew; i += 2){   //s
            if(pos[i] - 1 >= len){
                int R = len / 2;
                int position = i / 2 - 1;   //实际位置
                mp[getHashS(position - R, position + R)] = 1;
            }
        }
        for(int i = 0; i + len - 1 < lenp; i++){
            ull aim = getHashP(i, i + len - 1);
            if(mp.count(aim)) return true;
        }
        return false;
    }
    //$#c#a#a#a#a#0
    bool checkOu(int len){
        mp.clear();
        for(int i = 1; i + len - 1 < lenSnew; i += 2){   //s
            if(pos[i] - 1 >= len){
                int R = len / 2;
                int position = i / 2 - 1;   //实际位置
                mp[getHashS(position - R + 1, position + R)] = 1;
            }
        }
        for(int i = 0; i + len - 1 < lenp; i++){
            ull aim = getHashP(i, i + len - 1);
            if(mp.count(aim)) return true;
        }
        return false;
    }
    int main(){
        fac[0] = 1;
        for(int i = 1; i < 100010; i++)
            fac[i] = fac[i - 1] * seed;
        while(scanf("%s%s", s, p) != EOF){
            lens = lenp = 0;
            hs[0] = hp[0] = 0;
            for(int i = 0; s[i] != ''; i++){  //hash
                if(i == 0) hs[i] = s[i];
                else hs[i] = hs[i - 1] * seed + s[i];
                lens++;
            }
            for(int i = 0; p[i] != ''; i++){
                if(i == 0) hp[i] = p[i];
                else hp[i] = hp[i - 1] * seed + p[i];
                lenp++;
            }
    
            int R = Manacher(); //马拉车返回s最大长度
            int l, r, ans = 0;
            l = 1, r = R / 2;
            while(l <= r){  //
                int m = (l + r) >> 1;
                if(checkOu(m * 2)){
                    l = m + 1;
                    ans = m * 2;
                }
                else r = m - 1;
            }
            l = ans / 2, r = R / 2;
            while(l <= r){  //
                int m = (l + r) >> 1;
                if(checkJi(m * 2 + 1)){
                    l = m + 1;
                    ans = max(m * 2 + 1, ans);
                }
                else r = m - 1;
            }
            printf("%d
    ", ans);
        }
        return 0;
    }

    代码:

    #include<cmath>
    #include<set>
    #include<queue>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include <iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const int maxn = 200000 + 10;
    const ull seed = 131;
    const int INF = 0x3f3f3f3f;
    const int MOD = 1000000007;
    char s[maxn], p[maxn], snew1[maxn], snew2[maxn];
    int pos[maxn], num[maxn];
    ull hs[maxn], hp[maxn], fac[maxn], q[maxn];
    inline ull getHashP(int l, int r){
        return hp[r] - (l == 0? 0 : hp[l - 1]) * fac[r - l + 1];
    }
    inline ull getHashS(int l, int r){
        return hs[r] - (l == 0? 0 : hs[l - 1]) * fac[r - l + 1];
    }
    int lens, lenp, lenSnew;
    int init(int len){
        int cnt = 0;
        snew1[cnt] = '$';
        for(int i = 0; i < len; i++){
            snew1[++cnt] = '#';
            snew1[++cnt] = s[i];
        }
        snew1[++cnt] = '#';
        snew1[++cnt] = '';
        return cnt;
    }
    int Manacher(){
        int cnt = init(lens);
        lenSnew = cnt;
        int id = 0, ans = -1;
        for(int i = 2; i < cnt; i++){
            if(pos[id] + id > i){
                pos[i] = min(pos[2 * id - i], pos[id] + id - i);
            }
            else pos[i] = 1;
            while(snew1[i - pos[i]] == snew1[i + pos[i]])
                pos[i]++;
            if(id + pos[id] < i + pos[i])
                id = i;
            ans = max(ans,pos[i] - 1);
        }
        return ans; //长度
    }
    bool mid(int l, int r, ull aim){
        while(l <= r){
            int m = (l + r) >> 1;
            if(q[m] >= aim){
                if(q[m] == aim) return true;
                r = m - 1;
            }
            else l = m + 1;
    
        }
        return false;
    }
    bool check(int len){
        int tol = 0;
        for(int i = 0; i + len - 1 < lenp; i++){    //p子串
            q[tol++] = getHashP(i, i + len - 1);
        }
        sort(q, q + tol);
        for(int i = 2; i < lenSnew; i += 2){  //找s
            if(pos[i] - 1 >= len){
                int R = len / 2;
                int position = i / 2 - 1;   //实际位置
                ull aim = getHashS(position - R, position + R);
                if(mid(0, tol - 1, aim)){
                    return true;
                }
    
            }
        }
        return false;
    }
    int main(){
        fac[0] = 1;
        for(int i = 1; i < maxn; i++)
            fac[i] = fac[i - 1] * seed;
        while(scanf("%s%s", snew1, snew2) != EOF){
            lens = 0, lenp = 0;
    
            //改成全奇回文
            s[lens++] = '0';
            for(int i = 0; snew1[i] != ''; i++){
                s[lens++] = snew1[i];
                s[lens++] = '0';
            }
            p[lenp++] = '0';
            for(int i = 0; snew2[i] != ''; i++){
                p[lenp++] = snew2[i];
                p[lenp++] = '0';
            }
    
    
            hs[0] = hp[0] = 0;
            for(int i = 0; i < lens; i++){  //hash
                if(i == 0) hs[i] = s[i];
                else hs[i] = hs[i - 1] * seed + s[i];
            }
            for(int i = 0; i < lenp; i++){
                if(i == 0) hp[i] = p[i];
                else hp[i] = hp[i - 1] * seed + p[i];
            }
    
            int cnt = lenSnew;
            int L = 1, R = Manacher(); //马拉车返回最大长度
            int l = 1, r = R / 2; //R = 2 * r + 1
            int ans = 0;
            while(l <= r){
                int m = (l + r) >> 1;
                if(check(2 * m + 1)){
                    ans = 2 * m + 1;
                    l = m + 1;
                }
                else{
                    r = m - 1;
                }
            }
            printf("%d
    ", ans / 2);
        }
        return 0;
    }
  • 相关阅读:
    Go从入门到精通——函数(function)——把函数作为值保存到变量中
    Go从入门到精通——列表(list)——可以快速增删的非连续空间的容器
    Go从入门到精通——数组(固定大小的连续空间)
    Go从入门到精通——切片(slice)(动态分配大小的连续空间)
    Go从入门到精通——映射(map)——建立事物关联的容器
    Go从入门到精通——类型别名(Type Alias)
    Go从入门到精通——匿名函数——没有函数名字的函数
    愿我如星,子如月,夜夜流光,相皎洁 Kai
    文明6萌新初次过神标~纪念 Kai
    我们中国最伟大,最普遍,而且最永久的艺术就是男人扮女人。——鲁迅 Kai
  • 原文地址:https://www.cnblogs.com/KirinSB/p/10663751.html
Copyright © 2020-2023  润新知