【后缀自动机】hdu6194 string string string

题意:给你一个字符串和一个正整数K,让你输出恰好出现K次的子串的数量。

对后缀链接树进行dp预处理后,SAM每个点的endpos大小就是该点结尾的子串出现的次数,maxlen-minlen+1就是子串的数量,所以直接对endpos大小为K的点的(maxlen-minlen+1)求和即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXL 100000
#define MAXC 26
int v[2*MAXL+10],__next[2*MAXL+10],first[2*MAXL+10],e;
void AddEdge(int U,int V){
    v[++e]=V;
    __next[e]=first[U];
    first[U]=e;
}
char s[MAXL+10];
int len;
struct SAM{
    int endcnt[2*MAXL+10];
    int n,maxlen[2*MAXL+10],minlen[2*MAXL+10],trans[2*MAXL+10][MAXC],slink[2*MAXL+10];
    void clear(){
    	for(int i=0;i<=n;++i){
    		endcnt[i]=maxlen[i]=minlen[i]=slink[i]=first[i]=0;
    		memset(trans[i],0,sizeof(trans[i]));
    	}
    	n=e=0;
    }
    int new_state(int _maxlen,int _minlen,int _trans[],int _slink){
        maxlen[n]=_maxlen;
        minlen[n]=_minlen;
        for(int i=0;i<MAXC;++i){
            if(_trans==NULL){
                trans[n][i]=-1;
            }
            else{
                trans[n][i]=_trans[i];
            }
        }
        slink[n]=_slink;
        return n++;
    }
    int add_char(char ch,int u){
        if(u==-1){
            return new_state(0,0,NULL,-1);
        }
        int c=ch-‘a‘;
        int z=new_state(maxlen[u]+1,-1,NULL,-1);
        endcnt[z]=1;
        int v=u;
        while(v!=-1 && trans[v][c]==-1){
            trans[v][c]=z;
            v=slink[v];
        }
        if(v==-1){
            minlen[z]=1;
            slink[z]=0;
            return z;
        }
        int x=trans[v][c];
        if(maxlen[v]+1==maxlen[x]){
            minlen[z]=maxlen[x]+1;
            slink[z]=x;
            return z;
        }
        int y=new_state(maxlen[v]+1,-1,trans[x],slink[x]);
        slink[y]=slink[x];
        minlen[x]=maxlen[y]+1;
        slink[x]=y;
        minlen[z]=maxlen[y]+1;
        slink[z]=y;
        int w=v;
        while(w!=-1 && trans[w][c]==x){
            trans[w][c]=y;
            w=slink[w];
        }
        minlen[y]=maxlen[slink[y]]+1;
        return z;
    }
    void dfs(int U){
        for(int i=first[U];i;i=__next[i]){
            dfs(v[i]);
            endcnt[U]+=endcnt[v[i]];
        }
    }
    void work_slink_tree(){
        for(int i=1;i<n;++i){
            AddEdge(slink[i],i);
        }
        dfs(0);
    }
}sam;
int anss[MAXL+10];
int T,K;
typedef long long ll;
int main(){
//	freopen("a.in","r",stdin);
	scanf("%d",&T);
	for(;T;--T){
		scanf("%d%s",&K,s);
		len=strlen(s);
   		int U=sam.add_char(0,-1);
    	for(int i=0;i<len;++i){
        	U=sam.add_char(s[i],U);
    	}
    	sam.work_slink_tree();
    	ll ans=0;
    	for(int i=1;i<sam.n;++i){
    		if(sam.endcnt[i]==K){
    			ans+=(ll)(sam.maxlen[i]-sam.minlen[i]+1);
    		}
    	}
        printf("%d\n",ans);
		sam.clear();
	}
    return 0;
}
时间: 2024-12-29 12:47:15

【后缀自动机】hdu6194 string string string的相关文章

hdu 5008 Boring String Problem(后缀自动机构造后缀树)

hdu 5008 Boring String Problem(后缀自动机构造后缀树) 题意:给出一个字符串s,然后每次询问一个k,求s的所有子串中,字典序第k小的是谁?多个解,则输出最左边的那个 解题思路:这道题应该是为后缀树量身定制的吧.只要构造出了后缀树,然后按字典序遍历就可以得出每个节点包含的子串的字典序的范围了,而且必然是个连续的区间范围.但是我不会后缀树啊..比赛的时候突然想到,后缀自动机是可以构造后缀树的,虽然以前没写过,但还是硬着头皮上吧,居然还真的让我给撸出来了.我的做法是这样的

hdu 5853 Jong Hyok and String(广义后缀自动机)

题目链接:hdu 5853 Jong Hyok and String 题意: 给你n个字符串,m个询问,每次询问一个字符串 定义set(s)={(i,j)} 表示 s在第i个字符串中出现,且末尾位置为j. 对于一个询问,求set(Qi)=set(t) ,t串的数量. 题解: 如果是n=1,那么就是后缀自动机的一道裸题,答案就是Qi串匹配的最后一个节点x,ml[x]-ml[f[x]]. 现在是多个串,那么就建立一个广义后缀自动机.每次插入一个串后,将last=root,然后继续插下一个就行了. 最

[2018 ACM-ICPC 焦作赛区网络赛] H - String and Times(后缀自动机)

Now you have a string consists of uppercase letters, two integers AA and BB. We call a substring wonderful substring when the times it appears in that string is between AA and BB (A \le times \le BA≤times≤B). Can you calculate the number of wonderful

【hihocoder#1413】Rikka with String 后缀自动机 + 差分

题目链接:http://hihocoder.com/problemset/problem/1413 这个题非常的劲! 首先可以发现,每次只变换一个字符为#,所以每次答案一定会得到相应的包含#的答案,而这个方案是可以直接计算出来的. 假设是$S[i]=$#则会得到$i*(N-i+1)$的子串数. 所以每次的答案可以表示为$sum[root]+i*(N-i+1)-ans[i]$,其中$ans[i]$表示严格经过$i$位置的本质不同的子串,严格的意义即这个本质不同的子串有且仅有一次,且经过$i$: 所

&lt;string&gt; 与&lt;string.h&gt;、&lt;cstring&gt;的区别

原文地址:http://blog.csdn.net/luoweifu/article/details/20242307 <string.h> <string.h>是C版本的头文件,包含比如strcpy.strcat之类的字符串处理函数. <cstring> 在C++标准化(1998年)过程中,为了兼容以前,标准化组织将所有这些文件都进行了新的定义,加入到了标准库中,加入后的文件名就新增了一个"c"前缀并且去掉了.h的后缀名,所以string.h头文件

再探Java基础——String.format(String format, Object… args)的使用

最近看到类似这样的一些代码:String.format("参数%s不能为空", "birthday"); 以前还没用过这功能不知咐意思,后研究了一下,详细讲解如下. public static String format(String format, Object... args)的功能非常强大,用法非常灵活.主要的意思是返回指定的格式化的字符串.Format参数为格式字符串语法如下: %[argument_index$][flags][width][.precis

ArrayList、string、string[]之间的转换

1.ArrarList 转换为 string[] : ArrayList list = new ArrayList(); list.Add("aaa"); list.Add("bbb"); string[] arrString = (string[])list.ToArray(typeof( string)) ; 2.string[] 转换为 ArrarList : ArrayList list = new ArrayList(new string[] { &quo

C#中string和String的区别

从位置上讲: 1.大写String是.NET Framework里的String,小写string是C#语言中的string 2.如果删掉using System;,就不能使用大写的String,System是.NET Framework类库中的一个函数名 从性质上讲: 1.string是关键字,String是类,string不能作为类.结构.枚举.字段.变量.方法.属性的名称 2.用C#编写代码尽量使用小写string,比较符合规范.如果在追求效率的情况下可以使用大写的String,因为最终通

String、String.valueOf、toString 它们三者的区别总结

今天在使用这个的时候发现,他们三者好像在某些场所都是可以用的,但是不免会让人想到那既然它们三者这么的相似,那么总有些什么区别吧.我也在网上找了一些资料看.自己也看了API文档,就将他们三的区别总结一下吧.用了这么长时间,才发现有这些的不同,也是挺惭愧的. 先说他们三的作用是什么吧:没什么特别的作用,就是让我们得到的对象或参数类型,按照要求转成字符串的形式. String:毫无疑问,这种就是强转形式,简单方便,效率高.java程序员可能看到效率高或许有些激动,但是它有他的不好,那就是局限性.在ja

(String)将一个String里面的单词反转

e.g.  i love java    return   java love i public static String reverseStr(String str) { String[] strs=str.split(" "); StringBuilder sb=new StringBuilder(); for(int i=strs.length-1;i>=0;i--) { sb.append(strs[i]+" "); } return sb.toSt