Codeforces-CF-126B-Codeforces Beta Round #93 (Div. 1 Only) B. Password 题解 Z函数(拓展KMP)

本文介绍了如何使用Z函数解决一个字符串题目,要求找到一个字符串的最长子串,该子串既是其自身的后缀也是前缀,但不能是整个字符串的后缀。通过解析题目、分析思路,利用Z函数进行求解,并给出了C++代码实现。最后,当没有符合条件的子串时,输出'Just a legend'。
摘要由CSDN通过智能技术生成

题目(Problem)

英文题面

Asterix, Obelix and their temporary buddies Suffix and Prefix has finally found the Harmony temple. However, its doors were firmly locked and even Obelix had no luck opening them.
A little later they found a string s, carved on a rock below the temple’s gates. Asterix supposed that that’s the password that opens the temple and read the string aloud. However, nothing happened. Then Asterix supposed that a password is some substring t of the string s.
Prefix supposed that the substring t is the beginning of the string s; Suffix supposed that the substring t should be the end of the string s; and Obelix supposed that t should be located somewhere inside the string s, that is, t is neither its beginning, nor its end.
Asterix chose the substring t so as to please all his companions. Besides, from all acceptable variants Asterix chose the longest one (as Asterix loves long strings). When Asterix read the substring t aloud, the temple doors opened.
You know the string s. Find the substring t or determine that such substring does not exist and all that’s been written above is just a nice legend.

中文大意

给定一个字符串s,提取出最长的字串,这个字串同时等于字符串s的后缀和前缀,且这个字符串不能是字符串本身的后缀字串和前缀字串。
例如:ababab 可以分为 [ab][ab][ab] 分别编号为第一个是前缀,第三个是后缀,只有第二个既不是后缀也不是前缀,但是可以作为字符串的前缀和后缀(即和某前缀后缀字符串相等)

做题思路(Thinking)

我们可以发现,我们需要获取一个字符串同时作为前缀和后缀,所以我们可以提取一个后缀能作为字符串s的前缀,再来判断字符串里面是否有满足该条件的字符串。
这里我们可以使用Z函数,即z[i]标记从i开始的后缀字串与字符串s的前缀相同个数。当 i + z[i] == n 时,就可以发现从i开始的后缀可以整个作为字符串s的前缀。这样On扫一遍,从0到n就可以找出最大的合适字符串,再判断是否满足。
以下附上代码,代码中间看似是On^2的,但是可以发现,每当第一次进入(z[i] + i == n)即使跑完内部循环没找到答案,在第二次循环一定能再第一次循环处满足 z[j] >= z[i] 。

代码(Code)

#include <bits/stdc++.h>
#define endl '\n'

using namespace std;

vector<int> calcZ(string s) {
    int n = s.length();
    vector<int>z(n);
    for (int i = 1, l = 0, r = 0; i < n; ++i) {
        if (i <= r && z[i - l] < r - i + 1) {
            z[i] = z[i - l];
        }else {
            z[i] = max(0, r - i + 1);
            while (i + z[i] < n && s[z[i]] == s[i + z[i]]) z[i]++;
        }
        if (i + z[i] - 1 > r) r = i + z[i] - 1, l = i;
    }
    return z;
}

void solve() {
    string s;
    cin >> s;
    int n = s.length();
    vector<int>z = calcZ(s);
    for (int i = 0; i < n; ++i) {
        if (z[i] + i == n) {
            for (int j = 0; j < i; ++j) {
                if (z[j] >= z[i]) {
                    cout << s.substr(i) << endl;
                    return;
                }
            }
        }
    }
    cout << "Just a legend" << endl;
}

signed main(void) {
#ifdef DHDEBUG
    freopen("demo.in", "r", stdin);
    freopen("demo.out", "w", stdout);
    auto now = clock();
#endif
    cout << fixed << setprecision(10);
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    //int t; cin >> t; while (t--)
        solve();
#ifdef DHDEBUG
    cerr << "Program run for " << (clock() - now) / (double)CLOCKS_PER_SEC * 1000 << " ms.";
#endif
    return 0;
}

我的博客

欢迎大家来访问啊,快让我访问!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值