【洛谷1117_BZOJ4650】[NOI2016] 优秀的拆分(哈希_后缀数组_RMQ)

题目:

洛谷1117

分析:

定义把我校某兔姓神犇Tzz和他的妹子拆分,为“优秀的拆分”

随便写个哈希就能有\(95\)分的好成绩……

我的\(95\)分做法比fei较chang奇葩,不想浪费时间的可以忽略解法一qwq

解法一:

\(n\)个vector记录对于每个点\(i\),哪些长度\(len\)满足\(i+2len\leq n\)\(str[i, i+len)=str[i+len,i+2len)\)(即形如“\(AA\)”)。然后,枚举开头\(l\)和“\(AA\)”的长度\(len\),这种情况下的的答案就是以\(l+2len\)开头的“\(AA\)”数量(即“\(BB\)”)。

解法二:

这题哈希可以过的说qwq(虽然我惨遭卡常没过去)

预处理出以每个点开始和结尾的“\(AA\)”的数量\(st[i]\)\(ed[i]\),那么答案就是\(\sum\limits_{i=1}^{len-1}ed[i-1]st[i]\)

暴力枚举一个\(A\)的长度\(len\),然后每隔\(len\)个点作一个标记,共\(\lceil \frac {n}{len} \rceil\)个。可以发现任意长为\(2len\)的“\(AA\)”都会经过两个标记。枚举标记,计算相邻两标记\(a\)\(b\)开头的后缀的\(LCP\)(最长公共前缀)和两标记结尾的前缀的\(LCS\)(最长公共后缀)。如果\(LCP+LCS\geq len\)则存在一个“\(AA\)”串经过这两个标记,且这些串的起始位置是连续的。简单画个图就可以发现,它们的起始位置是\([a-LCS+1,a+LCP-len]\)。(脑补一下,第一个\(A\)的开头不能比\(a-LCS+1\)小,第一个\(A\)的结尾不能比\(a+LCP-1\)大。)用差分解决区间修改。

\(LCP\)\(LCS\)可以二分+哈希解决。根据某些神奇的原理,\(O(\sum\limits _{i=1}^n \frac{n}{i})=O(n\log n)\)(貌似叫调和级数)。里面再套个二分,复杂度\(O(n\log^2n)\)

解法三:

用后缀数组+ST表\(O(1)\)查询\(LCP\)\(LCS\),复杂度\(O(n\log n)\)。(什么,你不会后缀数组/不会用后缀数组查\(LCP\)\(LCS\)?戳我:【知识总结】后缀数组(Suffix_Array)

代码:

一、奇葩的哈希\(95\)分做法

五个月前写的代码,比较奇葩……

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
namespace zyt
{
    typedef long long ll;
    typedef pair<ll, ll> pll;
    const int N = 2010, seed = 131, p[2] = {(int)1e9 + 7, (int)1e9 + 9};
    ll F[N][2];
    ll h[N][2];
    void init()
    {
        F[0][0] = F[0][1] = 1;
        for (int i = 1; i < N; i++)
        {
            F[i][0] = F[i - 1][0] * seed % p[0];
            F[i][1] = F[i - 1][1] * seed % p[1];
        }
    }
    void init(const string &str)
    {
        h[0][0] = h[0][1] = str[0] - 'a';
        for (int t = 0; t < 2; t++)
            for (int i = 1; i < str.size(); i++)
                h[i][t] = (h[i - 1][t] * seed + str[i] - 'a') % p[t];
    }
    pll Hash(const string &str)
    {
        ll ans[2] = {0, 0};
        for (int t = 0; t < 2; t++)
            for (int i = 0; i < str.size(); i++)
                ans[t] = (ans[t] * seed + str[i] - 'a') % p[t];
        return make_pair(ans[0], ans[1]);
    }
    pll Hash(const int l, const int r)
    {
        if (l == 0)
            return make_pair(h[r][0], h[r][1]);
        else
        {
            int len = r - l + 1;
            return make_pair(
                (h[r][0] - h[l - 1][0] * F[len][0] % p[0] + p[0]) % p[0],
                (h[r][1] - h[l - 1][1] * F[len][1] % p[1] + p[1]) % p[1]);
        }
    }
    vector<int>repeat[N];
    void work()
    {
        ios::sync_with_stdio(false);
        int T;
        init();
        cin >> T;
        while (T--)
        {
            int ans = 0;
            string str;
            cin >> str;
            init(str);
            for (int l = 0; l < str.size(); l++)
            {
                vector<int>().swap(repeat[l]);
                for (int r = l; r < str.size(); r++)
                {
                    int len = r - l + 1;
                    if (r + len < str.size() && Hash(l, r) == Hash(r + 1, r + len)) 
                        repeat[l].push_back(len * 2);
                }
            }
            for (int l = 0; l < str.size(); l++)
                for (int i = 0; i < repeat[l].size(); i++)
                {
                    int r = l + repeat[l][i];
                    if (r < str.size())
                        ans += repeat[r].size();
                }
            cout << ans << endl;
        }
    }
}
int main()
{
    zyt::work();
    return 0;
}

二、\(O(n\log^2 n)\)的哈希期望\(100\)实际\(95\)的做法

(我是哪里写挂了还是常数太大啊qwq,\(O(n\log ^2n)\)凭什么过不了\(30000\)啊,我周围一圈神仙都能过的qwq

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
using namespace std;
namespace zyt
{
    typedef long long ll;
    typedef pair<int, int> pii;
    typedef pair<ll, ll> pll;
    const int N = 3e4 + 10;
    namespace Hash
    {
        typedef pii hash_t;
        const hash_t seed = pii(29, 29), p = pii(1e9 + 7, 1e9 + 9);
        pii operator + (const pii &a, const pii &b)
        {
            return make_pair(a.first + b.first, a.second + b.second);
        }
        pii operator - (const pii &a, const pii &b)
        {
            return make_pair(a.first - b.first, a.second - b.second);
        }
        pll operator * (const pii &a, const pii &b)
        {
            return make_pair((ll)a.first * b.first, (ll)a.second * b.second);
        }
        pii operator % (const pll &a, const pii &p)
        {
            return make_pair(a.first % p.first, a.second % p.second);
        }
        hash_t h[N], pow[N];
        inline void init()
        {
            pow[0] = make_pair(1, 1);
            for (int i = 1; i < N; i++)
                pow[i] = pow[i - 1] * seed % p;
        }
        inline int ctoi(const char c)
        {
            return c - 'a';
        }
        inline hash_t ctoh(const char c)
        {
            return make_pair(ctoi(c), ctoi(c));
        }
        inline void get_hash(const string &s)
        {
            h[0] = ctoh(s[0]);
            for (int i = 1; i < s.size(); i++)
                h[i] = (h[i - 1] * seed % p + ctoh(s[i])) % p;
        }
        inline hash_t extract(const int l, const int r)
        {
            if (l == 0)
                return h[r];
            else
                return (h[r] - h[l - 1] * pow[r - l + 1] % p + p) % p;
        }
    }
    int st[N], ed[N];
    inline int lcp(const int len, const int a, const int b)
    {
        using Hash::extract;
        int l = 1, r = len, ans = 0;
        while (l <= r)
        {
            int mid = (l + r) >> 1;
            if (extract(a, a + mid - 1) == extract(b, b + mid - 1))
                l = mid + 1, ans = mid;
            else
                r = mid - 1;
        }
        return ans;
    }
    inline int lcs(const int len, const int a, const int b)
    {
        using Hash::extract;
        int l = 1, r = len, ans = 0;
        while (l <= r)
        {
            int mid = (l + r) >> 1;
            if (extract(a - mid + 1, a) == extract(b - mid + 1, b))
                l = mid + 1, ans = mid;
            else
                r = mid - 1;
        }
        return ans;
    }
    int work()
    {
        ios::sync_with_stdio(false);
        int T;
        Hash::init();
        cin >> T;
        while (T--)
        {
            string str;
            cin >> str;
            memset(st, 0, sizeof(int[str.size()]));
            memset(ed, 0, sizeof(int[str.size()]));
            Hash::get_hash(str);
            for (int i = 1; i < str.size(); i++)
                for (int j = 0; j + i < str.size(); j += i)
                {
                    int nxt = j + i;
                    int pre = lcp(min(i, (int)str.size() - nxt + 1), j, nxt);
                    int suf = lcs(min(i, j + 1), j, nxt);
                    int sta = min(j - suf + 1, (int)str.size() - (i << 1));
                    int end = min(j + pre - i, (int)str.size() - (i << 1));
                    if (pre + suf - 1 >= i)
                    {
                        ++st[sta], --st[end + 1];
                        ++ed[sta + (i << 1) - 1], --ed[end + (i << 1)];
                    }
                }
            for (int i = 1; i < str.size(); i++)
                st[i] += st[i - 1], ed[i] += ed[i - 1];
            ll ans = 0;
            for (int i = 1; i < str.size(); i++)
                ans += (ll)ed[i - 1] * st[i];
            cout << ans << '\n';
        }
        return 0;
    }
}
int main()
{
    return zyt::work();
}

三、\(O(n\log n)\)的后缀数组优秀做法

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
using namespace std;
namespace zyt
{
    typedef long long ll;
    typedef pair<int, int> pii;
    typedef pair<ll, ll> pll;
    const int N = 3e4 + 10, B = 15, CH = 26;
    struct ST
    {
        int st[N][B];
        const int *arr;
        static int lg2[N];
        static bool lg2_built;
        int min(const int a, const int b)
        {
            return arr[a] < arr[b] ? a : b;
        }
        void build(const int n, const int *_arr)
        {
            arr = _arr;
            if (!lg2_built)
            {
                int tmp = 0;
                for (int i = 0; i < N; i++)
                {
                    lg2[i] = tmp;
                    if (i == (1 << (tmp + 1)))
                        ++tmp;
                }
                lg2_built = true;
            }
            for (int i = n - 1; i >= 0; i--)
            {
                st[i][0] = i;
                for (int j = 1; j <= lg2[n]; j++)
                    if (i + (1 << j) - 1 < n)
                        st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
                    else
                        break;
            }
        }
        inline int query(const int l, const int r)
        {
            int len = lg2[r - l + 1];
            return min(st[l][len], st[r - (1 << len) + 1][len]);
        }
    };
    int ST::lg2[N];
    bool ST::lg2_built;
    struct Suffix_Array
    {
        int sa[N], rank[N], tp[N], count[N], height[N], kind, len;
        ST st;
        inline int min(const int a, const int b)
        {
            return height[a] < height[b] ? a : b;
        }
        void radix_sort()
        {
            static int count[N];
            memset(count, 0, sizeof(int[kind]));
            for (int i = 0; i < len; i++)
                ++count[rank[tp[i]]];
            for (int i = 1; i < kind; i++)
                count[i] += count[i - 1];
            for (int i = len - 1; i >= 0; i--)
                sa[--count[rank[tp[i]]]] = tp[i];
        }
        void build(const string &s)
        {
            len = s.size();
            for (int i = 0; i < len; i++)
                rank[i] = s[i] - 'a', tp[i] = i;
            kind = CH;
            radix_sort();
            for (int tmp = 1; tmp < len; tmp <<= 1)
            {
                int cnt = 0;
                for (int i = len - tmp; i < len; i++)
                    tp[cnt++] = i;
                for (int i = 0; i < len; i++)
                    if (sa[i] >= tmp)
                        tp[cnt++] = sa[i] - tmp;
                radix_sort();
                swap(rank, tp);
                rank[sa[0]] = 0;
                kind = 1;
                for (int i = 1; i < len; i++)
                    if (tp[sa[i]] == tp[sa[i - 1]] && 
                        (sa[i] + tmp < len && sa[i - 1] + tmp < len) && 
                        (tp[sa[i] + tmp] == tp[sa[i - 1] + tmp]))
                        rank[sa[i]] = rank[sa[i - 1]];
                    else
                        rank[sa[i]] = kind++;
                if (kind == len)
                    break;
            }
            int k = 0;
            for (int i = 0; i < len; i++)
            {
                if (!rank[i])
                    continue;
                if (k)
                    --k;
                int j = sa[rank[i] - 1];
                while (i + k < len && j + k < len && s[i + k] == s[j + k])
                    ++k;
                height[rank[i]] = k;
            }
            st.build(len, height);
        }
    }sa1, sa2;
    int st[N], ed[N];
    inline int lcp(const int a, const int b)
    {
        int ra = sa1.rank[a], rb = sa1.rank[b];
        return sa1.height[sa1.st.query(min(ra, rb) + 1, max(ra, rb))];
    }
    inline int lcs(const int len, const int a, const int b)
    {
        int ra = sa2.rank[len - a - 1], rb = sa2.rank[len - b - 1];
        return sa2.height[sa2.st.query(min(ra, rb) + 1, max(ra, rb))];
    }
    int work()
    {
        ios::sync_with_stdio(false);
        int T;
        cin >> T;
        while (T--)
        {
            string str, rev;
            cin >> str;
            memset(st, 0, sizeof(int[str.size()]));
            memset(ed, 0, sizeof(int[str.size()]));
            sa1.build(str);
            rev = str, reverse(rev.begin(), rev.end());
            sa2.build(rev);
            for (int i = 1; i < str.size(); i++)
                for (int j = 0; j + i < str.size(); j += i)
                {
                    int nxt = j + i;
                    int pre = min(i, lcp(j, nxt));
                    int suf = min(i, lcs(str.size(), j, nxt));
                    int sta = min(j - suf + 1, (int)str.size() - (i << 1));
                    int end = min(j + pre - i, (int)str.size() - (i << 1));
                    if (pre + suf - 1 >= i)
                    {
                        ++st[sta], --st[end + 1];
                        ++ed[sta + (i << 1) - 1], --ed[end + (i << 1)];
                    }
                }
            for (int i = 1; i < str.size(); i++)
                st[i] += st[i - 1], ed[i] += ed[i - 1];
            ll ans = 0;
            for (int i = 1; i < str.size(); i++)
                ans += (ll)ed[i - 1] * st[i];
            cout << ans << '\n';
        }
        return 0;
    }
}
int main()
{
    return zyt::work();
}

猜你喜欢

转载自www.cnblogs.com/zyt1253679098/p/10129625.html