【洛谷3546_BZOJ2803】[POI2012]PRE-Prefixuffix(String Hash)

Problem:

洛谷3546

Analysis:

I gave up and saw other's solution when I had nearly thought of the method ... What a pity

Let's define a border of string \(s\) as a prefix \(p\) of \(s\) that \(p\) is also a suffix of \(s\), and \(p\) is not longer than half of \(s\). What the problem asked us to look for is a number \(L\), that the prefix of length \(L\) can be divided into two string \(s1\) and \(s2\) , and the suffix of length \(L\) can be divided into two string \(s2\) and \(s1\), so that this pair of prefix and suffix is cyclically equivalent. Obviously, \(s1\) is a border of string \(s\). Another fact is, if \(s1\) is of length \(len\), \(s2\) is a border of the substring \([len, n - len - 1]\). Define \(f[len]\) as the length of the maximum border of the substring \([len, n - len - 1]\) . Let's enumerate the length of \(s1\) as \(len\) brutely, and for all legal \(len\) ("legal" means the prefix of length \(len\) is a border of \(s\). We can check it by hashing in \(O(1)\) time), the answer is \(len + f[len]\).

Now the problem is how to calculate \(f[len]\). Brute force takes \(O(n^2)\) complexity, but the useful fact below can decrease the complexity to \(O(n)\) :

\[f[i]\leq f[i+1]+2\]

To make it easy, look at the (beautiful) picture below.

5c7e9d687bbdf.jpg

The first picture shows the situation when \(f[i]=f[i+1]+2\), and the second picture shows if \(f[i]\) (the black ones) is more than \(f[i+1]\) (the red ones) plus \(2\) , the \(f[i+1]\) must be wrong, for there's a longer border (the blue ones) of substring \([i+1, n-i-2]\).

Because of this fact, we can solve \(f[len]\) in \(O(n)\) time. We initialize \(f[i]\) as \(f[i+1]+2\), and decrease it until the substring \([i, n-i-1]\) has a border of length \(f[i]\). The proof of the complexity is similar to the one of solving \(height\) array by Suffix Array.

Code:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;

namespace zyt
{
    template<typename T>
    inline bool read(T &x)
    {
        char c;
        bool f = false;
        x = 0;
        do
            c = getchar();
        while (c != EOF && c != '-' && !isdigit(c));
        if (c == EOF)
            return false;
        if (c == '-')
            f = true, c = getchar();
        do
            x = x * 10 + c - '0', c = getchar();
        while (isdigit(c));
        if (f)
            x = -x;
        return true;
    }
    inline bool read(char *const s)
    {
        return ~scanf("%s", s);
    }
    template<typename T>
    inline void write(T x)
    {
        static char buf[20];
        char *pos = buf;
        if (x < 0)
            putchar('-'), x = -x;
        do
            *pos++ = x % 10 + '0';
        while (x /= 10);
        while (pos > buf)
            putchar(*--pos);
    }
    const int N = 1e6 + 10;
    int f[N], n;
    // f[i] is the maximum length of the border of substr[i, n - i - 1]
    char str[N];
    namespace Hash
    {
        typedef long long ll;
        typedef pair<int, int> pii;
        typedef pii hash_t;
        hash_t h[N], pow[N];
        const hash_t seed = hash_t(61, 67), p = hash_t(1e9 + 7, 1e9 + 9);
        hash_t operator + (const hash_t &a, const hash_t &b)
        {
            return hash_t((a.first + b.first) % p.first, (a.second + b.second) % p.second);
        }
        hash_t operator - (const hash_t &a, const hash_t &b)
        {
            return hash_t((a.first - b.first + p.first) % p.first, 
                (a.second - b.second + p.second) % p.second);
        }
        hash_t operator * (const hash_t &a, const hash_t &b)
        {
            return hash_t(int((ll)a.first * b.first % p.first), 
                int((ll)a.second * b.second % p.second));
        }
        void init()
        {
            pow[0] = make_pair(1, 1);
            for (int i = 1; i < N; i++)
                pow[i] = pow[i - 1] * seed;
        }
        inline int ctoi(const char c)
        {
            return c - 'a';
        }
        void get(const char *const s)
        {
            h[0] = make_pair(ctoi(s[0]), ctoi(s[0]));
            for (int i = 1; i < n; i++)
                h[i] = h[i - 1] * seed + make_pair(ctoi(s[i]), ctoi(s[i]));
        }
        hash_t extract(const int l, const int r)
        {
            return l ? (h[r] - h[l - 1] * pow[r - l + 1]) : h[r];
        }
    }
    using namespace Hash;
    void mk_f()
    {
        f[n >> 1] = 0;
        for (int i = (n >> 1) - 1; i >= 0; i--)
        {
            f[i] = min(f[i + 1] + 2, (n >> 1) - i);
            while (f[i] && extract(i, i + f[i] - 1) != extract(n - i - f[i], n - i - 1))
                --f[i];
        }
    }
    int work()
    {
        read(n), read(str);
        init();
        get(str);
        mk_f();
        int ans = 0;
        for (int i = 1; i <= (n >> 1); i++)
            if (extract(0, i - 1) == extract(n - i, n - 1))
                ans = max(ans, i + f[i]);
        write(ans);
        return 0;
    }
}
int main()
{
    return zyt::work();
}

转载于:https://www.cnblogs.com/zyt1253679098/p/10479425.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值