2023 ICPC Asia Regionals Online Contest. L Super-palindrome

202 阅读1分钟

题意

给字符串SS,求满足符合下面条件的子串个数。

条件:

  1. 该字符串能被分割成2k个部分,K1,k2,k3...k2kK_1,k_2,k_3...k_{2k}
  2. 每个部分前后相同,即下公式 (参考回文判断依据
  3. K1=K2k,K2=k2k1....K_1 = K_{2k}, K_2 = k_{2k-1}....
  4. S<=5e3|S| <= 5e3

思路

对于每个满足该条件的子串,其中心点可以枚举,两个指针向外拓展即可

比如下面这种情况,我们就能得到两个满足条件的子串。

因为每个子串的中心点是不会变化的,中心点相同的子串一定会被枚举到的

image.png

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
const int P = 13331;
typedef unsigned long long ull;
typedef long long ll;
ull p[N] = {1};
void solve()
{
    string s;
    cin >> s;
    s = "0" + s;
    int n = s.length() - 1;
    vector<ull> f(n + 1);
    for (int i = 1; i <= n; i++)
    {
        f[i] = f[i - 1] * P + s[i];
    }
    auto get = [&](int l, int r)
    {
        return f[r] - f[l - 1] * p[r - l + 1];
    };
    ll res = 0;
    for (int k = 1; k <= n; k++)
    {
        int i = k, i2 = k;
        int j = k + 1, j2 = k + 1;
        while (i >= 1 && j <= n)
        {
            if (get(i, i2) == get(j2, j))
            {
                res++;
                i2 = i - 1;
                j2 = j + 1;
            }
            j++, i--;
        }
    }
    cout << res << "\n";
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    for (int i = 1; i < N; i++)
        p[i] = p[i - 1] * P;
    while (t--)
    {
        solve();
    }
}