[TJOI2019]甲苯先生和大中锋的字符串

有个叫asuldb的神仙来嘲讽我

说这题SAM水题,而且SA过不了

然后我就用SA过了

显然是一个Height数组上长为k的滑块,判一下两边,差分一下就可以了

#include"cstdio"
#include"cstring"
#include"iostream"
#include"algorithm"
using namespace std;

const int MAXN=1e5+5;

int n,T,mx,hd,tl;
char ch[MAXN];
int id[MAXN],rnk[MAXN],SA[MAXN],bnk[MAXN],Ht[MAXN];
int cnt[MAXN],q[MAXN];

int read()
{
    int x=0;char ch=getchar();
    while(ch<'0'||'9'<ch) ch=getchar();
    while('0'<=ch&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x;
}

void shel()
{
    for(int i=1;i<=n;++i) ++bnk[rnk[i]];
    for(int i=1;i<=mx;++i) bnk[i]+=bnk[i-1];
    for(int i=1;i<=n;++i) SA[++bnk[rnk[id[i]]-1]]=id[i];
    for(int i=0;i<=mx;++i) bnk[i]=0;
}

void GetSA()
{
    mx=0;
    for(int i=1;i<=n;++i) id[i]=i,rnk[i]=ch[i],mx=mx<rnk[i]?rnk[i]:mx;
    shel();
    for(int k=1;k<n;k<<=1){
        for(int i=1;i<=k;++i) id[i]=n-k+i;
        int ct=k;
        for(int i=1;i<=n;++i) if(SA[i]>k) id[++ct]=SA[i]-k;
        shel();swap(rnk,id);rnk[SA[1]]=1;
        for(int i=2;i<=n;++i){
            if(id[SA[i]]==id[SA[i-1]]&&id[SA[i]+k]==id[SA[i-1]+k]) rnk[SA[i]]=rnk[SA[i-1]];
            else rnk[SA[i]]=rnk[SA[i-1]]+1;
        }if(rnk[SA[n]]==n) break;
        mx=rnk[SA[n]];
    }return;
}

void GetHt()
{
    int k=0;
    for(int i=1;i<=n;++i){
        if(rnk[i]==1) continue;
        int tmp=SA[rnk[i]-1];
        k=k?k-1:0;
        while(tmp+k<=n&&i+k<=n&&ch[i+k]==ch[tmp+k]) ++k;
        Ht[rnk[i]]=k;
    }return;
}

int GetLCP(int x,int y)
{
    if(x>y) return n-SA[y]+1;
    return Ht[q[hd]];
}

int main()
{
    T=read();
    while(T--){
        memset(id,0,sizeof(id));
        memset(cnt,0,sizeof(cnt));
        scanf("%s",ch+1);n=strlen(ch+1);
        int w;scanf("%d",&w);Ht[n+1]=0;
        GetSA(),GetHt();hd=1;tl=0;
        for(int i=2;i<=w;++i){
            while(hd<=tl&&Ht[q[tl]]>=Ht[i]) --tl;
            q[++tl]=i;
        }for(int i=w;i<=n;++i){
            if(i-q[hd]+1>=w) ++hd;
            while(hd<=tl&&Ht[q[tl]]>=Ht[i]) --tl;
            q[++tl]=i;
            int tmp=GetLCP(i+1,i+w-1);
            int g=max(Ht[i-w+1],Ht[i+1]);
            if(g<=tmp) ++cnt[g+1],--cnt[tmp+1];
        }int tmp=-1,mm=1;
        for(int i=1;i<=n;++i){
            cnt[i]+=cnt[i-1];
            if(cnt[i]>=mm) mm=cnt[i],tmp=i;
        }printf("%d\n",tmp);
    }return 0;
}

原文地址:https://www.cnblogs.com/AH2002/p/10805313.html

时间: 2024-08-30 18:22:19

[TJOI2019]甲苯先生和大中锋的字符串的相关文章

p5341 [TJOI2019]甲苯先生和大中锋的字符串

分析 TJOI白给题 建出sam,对于每个点如果它的子树siz和等于k 那么对于这个满足的点它有贡献的长度一定是一个连续区间 直接差分即可 代码 #include<bits/stdc++.h> using namespace std; int n,k,mx,ans,d[100100]; char s[100100]; struct SAM { int mp[200100][30],fa[200100],ed,ccnt,len[200100],siz[200100]; int head[2001

【TJOI2019】甲苯先生和大中锋的字符串

题目链接:https://www.luogu.com.cn/problem/P5341 题目大意:给定 \(T\) 个字符串, 分别求出这 \(T\) 个字符串中所有恰好出现 \(k\) 次的子串中 , 出现过最多次数的长度的最大值 solution 对每个字符串 , 先求出它的 \(height\) 数组 考虑恰好出现 \(k\) 次的子串 \(s\) , 对于 \(height\) 数组中连续的 \(h[i] ... h[i + k - 1]\) , 其必然满足 \(len(s) < min

【题解】Luogu P5340 [TJOI2019]大中锋的游乐场

原题传送门 没想到省选也会出这种题??! 实际就是一个带有限制的最短路 因为\(k<=10\),所以我们珂以暴力将每个点的权值分为[-k,k],为了方便我们珂以转化成[0,2k],将汉堡的权值记为1,可乐的权值记为-1,最短路即可,如果发现不合理的就果断扔掉即可(不知道有没有好事之徒用SPFA写) #include <bits/stdc++.h> #define N 10005 #define pi pair<int,int> #define getchar nc using

【题解】Luogu P5337 [TJOI2019]甲苯先生的字符串

原题传送门 我们设计一个\(26*26\)的矩阵\(A\)表示\(a~z\)和\(a~z\)是否能够相邻,这个矩阵珂以由\(s1\)得出.答案显然是矩阵\(A^{len_{s2}-1}\)的所有元素之和,矩阵快速幂即可 #include <bits/stdc++.h> #define ll long long #define mod 1000000007 using namespace std; inline void write(register int x) { if(!x)putchar

第十一届“蓝狐网络杯”湖南省大学生计算机程序设计竞赛 B - 大还是小? 字符串水题

B - 大还是小? Time Limit:5000MS     Memory Limit:65535KB     64bit IO Format: Description 输入两个实数,判断第一个数大,第二个数大还是一样大.每个数的格式为: [整数部分].[小数部分] 简单起见,整数部分和小数部分都保证非空,且整数部分不会有前导 0.不过,小数部分的最 后可以有 0,因此 0.0 和 0.000 是一样大的. Input 输入包含不超过 20 组数据.每组数据包含一行,有两个实数(格式如前所述)

大数据阶乘——字符串乘法器

char s[1001]; //字符串s存储乘法得到的大数字,s[0]代表低位 int GetLength()//返回s的最大不为空的元素下标 { int i; for(i=1000; i>=0; i--) { if(s[i]!='0') { break; } } return i; } void cal(int k)//计算大数字s和小数字k相乘 { int add=0;//进位数 int i; int temp; for(i=0; i<=GetLength(); i++)// { temp

2015福富福大笔试——实现字符串右移

前两天参加了福富在福大的宣讲会,并且参加了笔试,最后一道大题,这里讲一下当时我的解法,大概的题意是这个样子的,只能使用c的库,实现一个函数void MakeString(char *pStr,int n)(ps:这里的函数名是我现在取的,想不起来考题给的是什么了),函数要求是以'\0'结尾的字符串pStr,一个需要右移的字符个数n,实现类似输入这样MakeString("abcdefghi",2),字符串右移后变成:hiabcdeg: 以下给出我当时的解法,希望各位看官有什么意见或者建

[lintcode][美国大公司][1.字符串处理]

两个字符串是变位词 1 class Solution: 2 """ 3 @param s: The first string 4 @param b: The second string 5 @return true or false 6 """ 7 def anagram(self, s, t): 8 if len(s) == len(t): 9 d = {} 10 for i in range(len(s)): # full scan on e

luogu P5338 [TJOI2019]甲苯先生的滚榜

传送门 首先,排名系统,一看就知道是原题,可以上平衡树来维护 然后考虑一种比较朴素的想法,因为我们要知道排名在一个人前面的人数,也就是AC数比他多的人数+AC数一样并且罚时少的人数,所以考虑维护那两个东西.AC数更多的人数显然可以直接上树状数组.后者的话可以对每一种AC数开值域线段树,存每个罚时有多少人,注意到罚时之和不会超过\(1.5*10^6\),所以动态开点线段树可以轻松解决.然后每次有个人AC数和罚时改变就先在原来的位置-1,然后在新位置+1.每次询问就是树状数组上AC数\(>\)当前A