首页 > 其他分享 >Codeforces Beta Round 93 (Div. 1 Only) B. Password 一题三吃

Codeforces Beta Round 93 (Div. 1 Only) B. Password 一题三吃

时间:2024-10-17 14:01:22浏览次数:6  
标签:nxt int Codeforces len next 后缀 Only Round match

https://codeforces.com/problemset/problem/126/B

学完Z函数,先用哈希做了一遍,再用Z函数做了一遍,然后搜其他人的题解发现用next数组也能做,就又做了一遍

B. Password

题意

给一串字符串 \(s\),要求找一个最长的 \(t\)

\(t\) 既是 \(s\) 的前缀串,也是后缀串,还是除了前缀后缀外的一个子串

哈希

前缀和后缀同时哈希,如果匹配上了就根据这个长度遍历一遍 \(s\),看看能不能匹配上

注意要所有的前后缀都判一下,确保是最长的 \(t\) 串

const int N = 1e6 + 10;
const int p = 21839; // 以为卡哈希,随便找了个素数,结果是因为最大值
const int MOD = 1e9 + 7;
string s;
int n;
int hf[N]; // hf[i]=p^i
void prepare(int n) {
    hf[0] = 1;
    for (int i = 1; i <= n; i++) {
        hf[i] = (hf[i - 1] * p) % MOD;
    }
}

bool check(int sta, int hpre) {
    int hinit = hpre;
    for (int i = sta + 1; i < n - 1; i++) {
        hpre = ((hpre - s[i - sta - 1] * hf[sta]) % MOD + MOD) % MOD;
        hpre = (hpre * p % MOD) + s[i];
        if (hpre == hinit) {
            return true;
        }
    }
    return false;
}
void solveHash() {
    cin >> s;
    n = s.length();
    prepare(n);
    int ans = -1;
    int hpre = 0, haft = 0;
    for (int i = 0; i < n; i++) {
        hpre = (hpre * p + s[i]) % MOD;
        haft = (haft + (hf[i] * s[n - i - 1]) % MOD) % MOD;
        if (hpre == haft) {
            if (check(i, hpre)) {
                ans = i + 1;
            }
        }
    }
    if (ans != -1)
        cout << s.substr(0, ans);
    else
        cout << "Just a legend";
}

Z函数

这个其实只用 \(Z\) 数组,不用 \(e\) 数组,我推荐看左程云的讲解

从后往前看,如果 \(i+z[i]==n\) 说明 \(s[i\dots n-1]\) 是后缀也是前缀

接下来就是找有没有比 \(z[i]\) 更大的 \(z\) ,如果有,说明这个更大的 \(z\) 可以包含住这个后缀

const int N = 1e6 + 10;
int z[N];
int e[N];
string s, t;
void zArray(string &s, int n) {
    z[0] = n;
    for (int i = 1, c = 1, r = 1, len; i < n; i++) {
        len = r > i ? min(r - i, z[i - c]) : 0;
        while (i + len < n && s[i + len] == s[len]) len++;
        if (i + len > r) {
            r = i + len;
            c = i;
        }
        z[i] = len;
    }
}

void solveZ() {
    cin >> s;
    t = s;
    int n = s.length(), m = n;
    zArray(t, m);
    int ans = -1;

    set<int> match;
    // match中存储能与前缀匹配的后缀长度
    // 如果在match有比某个z[i]小的值x,说明这个x是答案
    for (int i = n - 1; i >= 1; i--) {
        if (match.size() > 0) {
            auto it = match.upper_bound(z[i]);
            // *it是大于z[i]的
            // 所以it往前的会小于等于z[i]
            if (it != match.begin()) {
                it--;
                ans = max(*it, ans);
            }
        }
        if (i + z[i] == n) match.insert(z[i]);
    }

    if (ans != -1) {
        cout << s.substr(0, ans);
    } else {
        cout << "Just a legend";
    }
}

KMP(用next数组)

\(next[i]\) 表示从\(s[0\dots i]\) 的最长相等前后缀

先把所有的 \(next\) 值存起来,然后 \(x=next[n-1]\)

如果该 \(x\) 出现过,就说明这是答案

如果该 \(x\) 没出现过,可能还存在较短的前后缀,也就是在 \(next[n-1]\) 这个长度内有重复结构

将 \(x=next[x-1]\),之所以是 \(x-1\),因为 \(x\) 表示的是字符串长度,对应下来的长度对应的 \(next\) 数组应该要减一位(如果你的 \(next\) 数组下标从 \(0\) 开始

const int N = 1e6 + 10;
int nxt[N];
string t;
void buildNext(string &t) {
    nxt[0] = 0;
    int i = 1;
    int nlen = 0;
    int tlen = t.length();
    while (i < tlen) {
        if (t[nlen] == t[i]) {
            nlen++;
            nxt[i] = nlen;
            i++;
        } else {
            if (nlen == 0) {
                nxt[i] = 0;
                i++;
            } else
                nlen = nxt[nlen - 1];
        }
    }
}

bool vis[N];
void solveKMP() {
    cin >> t;
    buildNext(t);
    int n = t.length();
    fill_n(vis, n + 5, false);
    for (int i = 1; i < n - 1; i++) {
        vis[nxt[i]] = 1;
    }
    int x = nxt[n - 1];
    while (!vis[x] && x != 0) {
        x = nxt[x - 1];
    }
    if (x != 0)
        cout << t.substr(0, x);
    else
        cout << "Just a legend";
}

标签:nxt,int,Codeforces,len,next,后缀,Only,Round,match
From: https://www.cnblogs.com/lulaalu/p/18472086

相关文章

  • 浏览器安装 AtCoder Better 和 Codeforces Better 插件
    你首先需要篡改猴。如果你用的Google浏览器,请用这个Link,不过你可能需要挂个梯子。如果你用的Firefox浏览器,请用这个Link,这个不需要梯子。如果你用的edge浏览器,请用这个Link,这个也不需要梯子。下载好篡改猴之后,无论什么浏览器,点击这个链接,安装AtCoderBetter插件;点......
  • CodeForces - 1364D
    通常的想法是:如果图是一棵树,那么通过对顶点进行双色染色,并从更频繁的颜色中选取顶点,就可以轻松找到大小为\(\lceil\frac{n}{2}\rceil\)的独立集合。否则,图就是循环的。让我们得到一个没有任何边“穿过”的循环。换句话说,它没有任何一对不相邻的顶点由边连接。如果它的长度最多......
  • 打卡信奥刷题(056)用C++工具信奥P10566[普及组/提高] 「Daily OI Round 4」Analysis
    「DailyOIRound4」Analysis题目描述小C的信息技术老师给小C布置了一项作业,作业内容如下:有一个字符串,包含大小写字母和数字。你可以把任意一个字符变成另外一个字符,设变化之前字符的ASCII码为a......
  • Go语言中http.Transport的RoundTrip方法请求过滤与拦截技巧与应用
    go语言中http.transport的请求过滤与拦截技巧与应用1.引言在Go语言的http包中,http.Transport作为底层的HTTP传输层实现,提供了强大的功能,可以用于发起HTTP请求。本文将重点介绍如何使用http.Transport实现请求过滤和拦截的技巧及其应用。2.请求过滤2.1过滤请求方法我们可以使用h......
  • Educational Codeforces Round 170 (Rated for Div. 2)
    A.TwoScreens难点是读题,找到最长公共前缀即可。#include<bits/stdc++.h>usingnamespacestd;usingi32=int32_t;usingi64=longlong;#defineinti64usingvi=vector<int>;usingpii=pair<int,int>;consti32inf=INT_MAX/2;constintm......
  • Educational Codeforces Round 170 (Rated for Div. 2) ABCD
    来源:EducationalCodeforcesRound170(RatedforDiv.2)A.TwoScreens题意给两个屏幕,两种操作,每种操作一秒,求让两个屏幕显示两个指定字符串的最短时间操作:在一个屏幕的字符串后加任意一个字符把一个屏幕的内容复制粘贴到另一个屏幕思路先弄出相同前缀,复制一下,然后不......
  • Codeforces Round 978 (Div. 2) C. Gerrymandering 轮廓DP
    CodeforcesRound978(Div.2)C轮廓DPC.Gerrymandering思路:考虑有哪些情况呢?发现结尾只有三种情况,0.平的,1.上凸,2.下凸。那么每一种后面能出现什么呢?这样看起来就好写啦。//AConemoretimes//nndbk#include<bits/stdc++.h>usingnamespacestd;typedeflonglo......
  • 【做题记录】Codeforces Round 943 (Div. 3)/CF1968A-F
    【做题记录】CodeforcesRound943(Div.3)/CF1968A-FA暴力枚举即可。B双指针枚举即可,能匹配就匹配。C考虑构造出\(a[1]=1,a[i]=a[i-1]+x[i]\)的数列,发现满足要求。D有个明显的结论,两人最终一定是在某个点上的。于是从起点开始扫一遍,求出到每一个点的距离和路上的分数......
  • Solution - Codeforces 1628D2 Game on Sum (Hard Version)
    首先来考虑Easy。注意到的是最后输出的答案要求是模意义下的,这说明对于实数二分的做法都已经用不了了。注意到\(n,m\le3000\)的数据范围,于是一个想法就是考虑DP之类的算法。考虑到B选了\(+/-\)实际上就代表着下一轮的\(m\)是否会\(-1\),于是可以设状态为\(f_{i......
  • 「题解」Educational Codeforces Round 170 (Rated for Div. 2)
    before我不想写作业呜呜。A.TwoScreensProblemA.TwoScreensSol&Code理解题意后发现使用复制的方法完成最长公共前缀即可。#include<bits/stdc++.h>typedeflonglongll;typedefstd::pair<int,int>pii;intT;std::strings1,s2;intmain(){scanf("%d"......