2013多校联合4 1010 K-string (hdu 4641)
2013-08-01 20:04
387 查看
http://acm.hdu.edu.cn/showproblem.php?pid=4641
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 102400/131072 K (Java/Others)
Total Submission(s): 24 Accepted Submission(s): 11
Problem Description
Given a string S. K-string is the sub-string of S and it appear in the S at least K times.It means there are at least K different pairs (i,j) so that Si,Si+1... Sj equal to this K-string. Given m operator or query:1.add a letter
to the end of S; 2.query how many different K-string currently.For each query ,count the number of different K-string currently.
Input
The input consists of multiple test cases.
Each test case begins with a line containing three integers n, m and K(1<=n,K<=50000,1<=m<=200000), denoting the length of string S, the number of operator or question and the least number of occurrences of K-string in the S.
The second line consists string S,which only contains lowercase letters.
The next m lines describe the operator or query.The description of the operator looks as two space-separated integers t c (t = 1; c is lowercase letter).The description of the query looks as one integer t (t = 2).
Output
For each query print an integer — the number of different K-string currently.
思路:赛后才发现,其实过这道题完全是因为运气好,题目数据不强,赛后被光林大神的一个极限数据卡了几十秒都没跑出来。。。不过还是说说我的思路吧,我使用后缀自动机写的,首先在后缀自动机的每个节点中除了常规的val外,再设置两个变量,num表示这个节点所表示的子串在当前字符串中所出现的次数,flag表示该节点所表示的子串是否已经超过K,那么我们在将一个字符加入后缀自动机时,更新ans,因为一个子串的出现次数只和它的right集合有关,而只有当一个节点,有新的节点的par指针指向它时,它的right集合才会改变,多以我们只要将一个新节点加入原SAM后,从它开始沿着par指针一直向上更新,因为一个节点的父节点的出现次数一定比该节点大,所以一个显然易见的优化就是当走到一个节点,它的num已经不小于K时就停止(即flag为1)。虽然这样的程序已经可以过这道题的所有数据,但是事实上还是有数据惠超时,比如对于k很大时,且加入的字符全是一样的,这样的话,构造的后缀自动机就会是一条链,那么我们更新节点时,会一直遍历很多次才能结束,所以必定超时,对于这样的数据,我的一个想法是用一个数据结构(平衡树,树状数组,线段树什么的)将SAM存起来,然后利用该数据结构更新节点即可,但是还没有实践,这里先贴一个最原始的代码,仅供参考。
K-string
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 102400/131072 K (Java/Others)Total Submission(s): 24 Accepted Submission(s): 11
Problem Description
Given a string S. K-string is the sub-string of S and it appear in the S at least K times.It means there are at least K different pairs (i,j) so that Si,Si+1... Sj equal to this K-string. Given m operator or query:1.add a letter
to the end of S; 2.query how many different K-string currently.For each query ,count the number of different K-string currently.
Input
The input consists of multiple test cases.
Each test case begins with a line containing three integers n, m and K(1<=n,K<=50000,1<=m<=200000), denoting the length of string S, the number of operator or question and the least number of occurrences of K-string in the S.
The second line consists string S,which only contains lowercase letters.
The next m lines describe the operator or query.The description of the operator looks as two space-separated integers t c (t = 1; c is lowercase letter).The description of the query looks as one integer t (t = 2).
Output
For each query print an integer — the number of different K-string currently.
思路:赛后才发现,其实过这道题完全是因为运气好,题目数据不强,赛后被光林大神的一个极限数据卡了几十秒都没跑出来。。。不过还是说说我的思路吧,我使用后缀自动机写的,首先在后缀自动机的每个节点中除了常规的val外,再设置两个变量,num表示这个节点所表示的子串在当前字符串中所出现的次数,flag表示该节点所表示的子串是否已经超过K,那么我们在将一个字符加入后缀自动机时,更新ans,因为一个子串的出现次数只和它的right集合有关,而只有当一个节点,有新的节点的par指针指向它时,它的right集合才会改变,多以我们只要将一个新节点加入原SAM后,从它开始沿着par指针一直向上更新,因为一个节点的父节点的出现次数一定比该节点大,所以一个显然易见的优化就是当走到一个节点,它的num已经不小于K时就停止(即flag为1)。虽然这样的程序已经可以过这道题的所有数据,但是事实上还是有数据惠超时,比如对于k很大时,且加入的字符全是一样的,这样的话,构造的后缀自动机就会是一条链,那么我们更新节点时,会一直遍历很多次才能结束,所以必定超时,对于这样的数据,我的一个想法是用一个数据结构(平衡树,树状数组,线段树什么的)将SAM存起来,然后利用该数据结构更新节点即可,但是还没有实践,这里先贴一个最原始的代码,仅供参考。
#include <iostream> #include <string.h> #include <stdio.h> #define maxn 500010 #define ll long long #define Smaxn 26 using namespace std; struct node { node *par,*go[Smaxn]; int num,flag; int val; }*root,*tail,que[maxn]; int tot; ll ans=0; int n,m,k; void add(int c,int l) { node *p=tail,*np=&que[tot++]; np->val=l; np->num=1; if(k==1) { np->flag=1; ans++; } while(p&&p->go[c]==NULL) p->go[c]=np,p=p->par; if(p==NULL) np->par=root; else { node *q=p->go[c]; if(p->val+1==q->val) { np->par=q; node *tmp=q; while(tmp!=root) { if(tmp->flag==1) break; tmp->num++; if(tmp->num>=k&&tmp->flag==0) { ans+=tmp->val-tmp->par->val; tmp->flag=1; } tmp=tmp->par; } } else { node *nq=&que[tot++]; *nq=*q; nq->val=p->val+1; np->par=q->par=nq; while(p&&p->go[c]==q) p->go[c]=nq,p=p->par; node *tmp=nq; while(tmp!=root) { if(tmp->flag==1) break; tmp->num++; if(tmp->num>=k&&tmp->flag==0) { ans+=tmp->val-tmp->par->val; tmp->flag=1; } tmp=tmp->par; } } } tail=np; } char str[maxn>>1]; int len; void init(int n) { int i; for(i=0;i<=n;i++) { que[i].num=que[i].val=que[i].flag=0; que[i].par=NULL; memset(que[i].go,0,sizeof(que[i].go)); } ans=0; tot=0; len=1; root=tail=&que[tot++]; } int main() { // freopen("dd.txt","r",stdin); // freopen("output.txt","w",stdout); while(scanf("%d%d%d",&n,&m,&k)!=EOF) { scanf("%s",str); int i,l=strlen(str); init(2*(l+m)); for(i=0;i<l;i++) { add(str[i]-'a',len++); } int q; char tmp[2]; while(m--) { scanf("%d",&q); if(q==1) { scanf("%s",tmp); add(tmp[0]-'a',len++); } else printf("%I64d\n",ans); } } return 0; }
相关文章推荐
- 2013多校联合8 String (hdu 4681)
- 2013多校联合3 1010 No Pain No Game(hdu 4630)
- hdu 4699 2个栈维护 or 伸展树 (2013多校联合)
- HDU 4666 Hyperspace(2013多校联合7 1001)
- HDU 4669 Mutiples on a circle (2013多校联合7 1004)
- HDU 4630 No Pain No Game(2013多校3 1010题 离线处理+树状数组求最值)
- 2013多校联合 3 B Reincarnation (hdu 4622)
- hdu 4677 并查集+分块算法 好题 (2013多校联合)
- 2016多校联合训练赛 第三场1010 Rower Bo hdu 5761
- 2013 多校联合 I I-number(hdu 4608)
- hdu 4562 Dice 求期望 推数学公式 (2013多校联合)
- 2013多校联合2 I Warm up 2(hdu 4619)
- hdu 4677 并查集+分块算法 好题 (2013多校联合)
- HDU 4655 2013多校联合赛第6场 Cut Pieces
- 2013 多校联合 F Magic Ball Game (hdu 4605)
- 2013 多校联合 2 A Balls Rearrangement (hdu 4611)
- hdu 4607 park visit 2013多校联合训练第一场
- 2013 多校联合4 1007 Group(hdu 4638)
- HDU 4686 Arc of Dream (2013多校联合9 1001)
- hdu 4679 Terrorist’s destroy 树形dp水题 (2013多校联合)