str

2 篇文章 0 订阅
2 篇文章 0 订阅

题目大意

给定一个长度为n的字符串,求他有多少个本质不同的回文子串,并输出第k小的。
n<=10^5

分析

看到字符串果断被吓到然后跳过了,后来大佬一说发现这题还是很可做的,我们可以先用manacher求出本质不同的所有字符串,再通过二分的方法就可以log^2的对所有字符串进行排序了。

代码

#include <bits/stdc++.h>

typedef std::pair<long long, long long> lp;
typedef std::pair<int, int> p;
typedef long long ll;

const long long MOD1 = 1e9 + 7;
const long long MOD2 = 55566677;
const long long POW1 = 2333333;
const long long POW2 = 23333;
const int N = 200005;

ll hash1[N], hash2[N];
ll pow1[N], pow2[N];

lp getHash(int x,int y)
{
    ll h1 = (hash1[y] - 1ll * hash1[x - 1] * pow1[y - x + 1] % MOD1 + MOD1) % MOD1;
    ll h2 = (hash2[y] - 1ll * hash2[x - 1] * pow2[y - x + 1] % MOD2 + MOD2) % MOD2;
    return lp(h1, h2);
}

char a[N * 2];
int rad[N];

int n,k;
ll m;
char str[N];

std::map<lp, int> map;
std::vector<p> V;

void manacher()
{
    for (int i = 0; i < n; i++)
    {
        a[i * 2] = '#';
        a[i * 2 + 1] = str[i];
    }
    a[n * 2] = '#';
    int len = n * 2 + 1, pos = 0, mx = 0;
    int ans = 0;
    for (int i = 0; i < len; i++)
    {
        if (i < mx)
            rad[i] = std::min(rad[pos * 2 - i], mx - i);
        else rad[i] = 0;
        while (i - rad[i] > 0 && i + rad[i] + 1 < len && a[i - rad[i] - 1] == a[i + rad[i] + 1])
        {
            rad[i]++;
            lp h = getHash((i - rad[i]) / 2 + 1, (i + rad[i] - 1) / 2 + 1);
            if (!rad[i] && a[i] == '#' || map.count(h))
                continue;
            map[h] = 1;
            ans++;
            V.push_back(p((i - rad[i]) / 2, (i + rad[i] - 1) / 2));
        }
        if (i + rad[i] > mx)
            mx = i + rad[i], pos = i;
    }
    printf("%d\n",ans);
    k = m % ans + 1;
}

bool cmp(p a,p b)
{
    int lena = a.second - a.first + 1;
    int lenb = b.second - b.first + 1;
    if (lena != lenb)
        return lena < lenb;
    int l = 1, r = lena;
    while (l <= r)
    {
        int mid = (l + r) >> 1;
        lp h1 = getHash(a.first + 1, a.first + mid);
        lp h2 = getHash(b.first + 1, b.first + mid);
        if (h1 == h2)
            l = mid + 1;
        else r = mid - 1;
    }
    return str[a.first + r] < str[b.first + r];
}

int main()
{
    scanf("%d%lld",&n,&m);
    scanf("%s",str);
    pow1[0] = 1, pow2[0] = 1;
    for (int i = 1; i <= n; i++)
    {
        hash1[i] = (1ll * hash1[i - 1] * POW1 + str[i - 1]) % MOD1;
        hash2[i] = (1ll * hash2[i - 1] * POW2 + str[i - 1]) % MOD2;
        pow1[i] = pow1[i - 1] * POW1 % MOD1;
        pow2[i] = pow2[i - 1] * POW2 % MOD2;
    }
    manacher();
    std::sort(V.begin(), V.end(), cmp);
    for (int i = V[k - 1].first; i <= V[k - 1].second; i++)
        putchar(str[i]);
    putchar('\n');
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值