poj3693

  1 //Accepted    12004 KB    407 ms
  2 /*
  3     source:poj3693
  4     time  :20150819
  5     by    :songt
  6   */
  7 /*题解:
  8 搞了一天,总算弄完了
  9 首先,我们来明确一个问题 1.如果一个字符串S由一个子串S1长度为L重复K次得到,那么lcp(0,l)=(K-1)*L;
 10                               而如果一个字符串中存在lcp(i,i+L)=m,那么字符串中就存在重复m/L+1次的子串
 11                               这个可以画个图看下
 12 下面我们按照论文里的思路,枚举每个循环节的长度L,假设某个长度为L的子串在原字符中出现了两次以上,那么由
 13 容斥原理可知,这段连续重复的子串S一定包括了s[0],s[L],s[2*L],s[3*L],...中的连续的两个,这样我们可以枚举找到
 14 包括最开始的两个是哪两个,假设是s[i*L]和s[(i+1)*L],那么求lcp(i*L,(i+1)*L)=m,由1可知,原字符串中从i*L到(i+1)*L这段
 15 长度为L的子串,一定重复了m/L+1,但是由于i*L和(i+1)*L不一定是重复子串的第一个开始位置,即i*L不一定对应S[0],所以我们
 16 尝试调整开始的位置,假设i*L对应于S(0,L)中的某个字符,那么lcp(i*L,(i+1)*L)=m中的m就会比(m/L)*m大一点,这一点就是因为i*L
 17 不对应S[0],而对应了S(0,L)中的某个字符造成的,这样我们就可以知道,多匹配的这一点长度就对应(i*L对应于S[k] 0<k<L) k到L这一段
 18 长度,所以应该尝试把i*L向前移动L-m%L个字符(m%L!=0).这样我们就可以求出最大的重复次数。
 19 加入最多只重复了1次也就是没有重复,那么用上面的方法也可以求得。
 20
 21 接下来是求最小字典序的步骤,我们在求最大重复次数的时候,保存对应可能的长度L,那么我们可以从sa[1]到sa[n]枚举,
 22 如果sa[i]和某个长度L能够满足重复次数的要求,那么就得到了答案,枚举中遇到的第一个就是结果,应为sa[1]到sa[n]已经
 23 按照字典序排序
 24
 25   */
 26 #include <cstdio>
 27 #include <cstring>
 28 #include <vector>
 29 #include <algorithm>
 30 using namespace std;
 31
 32 const int imax_n = 100005;
 33 int wa[imax_n],wb[imax_n],wn[imax_n],wv[imax_n];
 34 int cmp(int *r,int a,int b,int l)
 35 {
 36     return r[a]==r[b] && r[a+l]==r[b+l];
 37 }
 38 void da(int *r,int *sa,int n,int m)
 39 {
 40     int i,j,k,p,*x=wa,*y=wb,*t;
 41     for (i=0;i<m;i++) wn[i]=0;
 42     for (i=0;i<n;i++) wn[x[i]=r[i]]++;
 43     for (i=1;i<m;i++) wn[i]+=wn[i-1];
 44     for (i=n-1;i>=0;i--) sa[--wn[x[i]]]=i;
 45     for (j=1,p=1;p<n;j*=2,m=p)
 46     {
 47         for (p=0,i=n-j;i<n;i++) y[p++]=i;
 48         for (i=0;i<n;i++) if (sa[i]>=j) y[p++]=sa[i]-j;
 49         for (i=0;i<n;i++) wv[i]=x[y[i]];
 50         for (i=0;i<m;i++) wn[i]=0;
 51         for (i=0;i<n;i++) wn[wv[i]]++;
 52         for (i=1;i<m;i++) wn[i]+=wn[i-1];
 53         for (i=n-1;i>=0;i--) sa[--wn[wv[i]]]=y[i];
 54         for (t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
 55         x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
 56     }
 57     return ;
 58 }
 59 int rank[imax_n];
 60 int height[imax_n];
 61 int a[imax_n];
 62 char s[imax_n];
 63 int sa[imax_n];
 64 int n;
 65 void calHeight(int *r,int *sa,int n)
 66 {
 67     int i,j,k=0;
 68     for (i=1;i<=n;i++) rank[sa[i]]=i;
 69     for (i=0;i<n;height[rank[i++]]=k)
 70     for (k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
 71     return ;
 72 }
 73
 74 int min(int a,int b)
 75 {
 76     return a<b?a:b;
 77 }
 78 int dp[imax_n][20];
 79 int mm[imax_n];
 80 void initRMQ(int n,int b[])
 81 {
 82     mm[0]=-1;
 83     for (int i=1;i<=n;i++)
 84     {
 85         mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
 86         dp[i][0]=b[i];
 87     }
 88     for (int j=1;j<=mm[n];j++)
 89     {
 90         for (int i=1;i+(1<<j)-1<=n;i++)
 91         {
 92             dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
 93         }
 94     }
 95 }
 96 int rmq(int x,int y)
 97 {
 98     x=rank[x];
 99     y=rank[y];
100     if (x>y)
101     {
102         int tmp=x;
103         x=y;
104         y=tmp;
105     }
106     x++;
107     int k=mm[y-x+1];
108     return min(dp[x][k],dp[y-(1<<k)+1][k]);
109 }
110
111 vector<int > vec;
112
113 void Deal()
114 {
115     n=strlen(s);
116     for (int i=0;i<n;i++)
117     {
118         a[i]=(int )s[i];
119     }
120     a[n]=0;
121     da(a,sa,n+1,130);
122     calHeight(a,sa,n);
123     initRMQ(n,height);
124     vec.clear();
125     int max_times=0;
126     for (int l=1;l<=n/2;l++)  //枚举循环节的长度
127     {
128         for (int i=0;i+l<n;i+=l)  //找对应子串S第一个循环节和第二个循环节的位置
129         {
130             int length=rmq(i,i+l);  //求出重复的次数
131             int times=length/l+1;
132             int newpos=i-(l-length%l);
133             if (newpos>=0 && length%l && rmq(newpos,newpos+l)>length) times++;  //尝试更新结果
134             if (times>max_times)
135             {
136                 vec.clear();
137                 vec.push_back(l);
138                 max_times=times;
139             }
140             else if (times==max_times)
141             {
142                 vec.push_back(l);
143             }
144         }
145     }
146     sort(vec.begin(),vec.end());
147     int cnt=unique(vec.begin(),vec.end())-vec.begin();
148     //printf("max_times=%d\n",max_times);
149     //for (int i=0;i<cnt;i++)
150     //{
151     //    printf("length=%d\n",vec[i]);
152     //}
153     int start,length;
154     //printf("size=%d\n",vec.size());
155
156     int flag=0;
157     for (int i=1;i<=n && !flag;i++)
158     {
159         for (int j=0;j<cnt && !flag;j++)
160         {
161             if (rmq(sa[i],sa[i]+vec[j])>=(max_times-1)*vec[j])
162             {
163                 start=sa[i];
164                 length=vec[j]*max_times;
165                 flag=1;
166             }
167         }
168     }
169     //printf("start=%d length=%d\n",start,length);
170     for (int i=start;i<start+length;i++)
171     printf("%c",s[i]);
172     printf("\n");
173 }
174
175 int main()
176 {
177     int T;
178     int t=0;
179     while (scanf("%s",s)!=-1)
180     {
181         if (strcmp(s,"#")==0) break;
182         printf("Case %d: ",++t);
183         Deal();
184     }
185     return 0;
186 }
时间: 2024-10-12 21:15:26

poj3693的相关文章

【poj3693】Maximum repetition substring(后缀数组+RMQ)

自己看着大牛的论文学了一下后缀数组,看了好久好久,想了好久好久才懂了一点点皮毛TAT 然后就去刷传说中的后缀数组神题,poj3693是进化版的,需要那个相同情况下字典序最小,搞这个搞了超久的说. 先简单说一下后缀数组.首先有几个重要的数组: ·SA数组(后缀数组):保存所有后缀排序后从小到大的序列.[即SA[i]=j表示排名第i的后缀编号为j]        ·rank数组(名次数组):记录后缀的名次.[即rank[i]=j表示编号为i的后缀排名第j] 用倍增算法可以在O(nlogn)时间内得出

POJ3693:Maximum repetition substring(后缀数组+RMQ)

Description The repetition number of a string is defined as the maximum number R such that the string can be partitioned into R same consecutive substrings. For example, the repetition number of "ababab" is 3 and "ababa" is 1. Given a

【POJ3693】Maximum repetition substring (SA)

这是一道神奇的题目..论文里面说得不清楚,其实是这样...如果一个长度为l的串重复多次,那么至少s[1],s[l+1],s[2*l+1],..之中有相邻2个相等...设这时为j=i*l+1,k=j+l,我们这时候借助SA和RMQ  O(1)求出:m=lcp(j,k),这时候,重复次数至少ans=m div l+1 .  当然,我们枚举到不一定能够是最优啊,因为你枚举的不一定是字符串的首尾..那这时候怎么办?就是论文里面说的,向前和向后匹配.我们设t=l-m mod l..可以理解为,这时候 m

poj3693 Maximum repetition substring 后缀数组

http://poj.org/problem?id=3693 Maximum repetition substring Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7241   Accepted: 2162 Description The repetition number of a string is defined as the maximum number R such that the string can b

[POJ3693]Maximum repetition substring

试题描述 The repetition number of a string is defined as the maximum number R such that the string can be partitioned into R same consecutive substrings. For example, the repetition number of "ababab" is 3 and "ababa" is 1. Given a string

【POJ3693】Maximum repetition substring 后缀数组恶心题

转载请注明出处:http://blog.csdn.net/vmurder/article/details/42677359 其实我就是觉得原创的访问量比未授权盗版多有点不爽233... 题意: 给一个字符串,然后找一个子串,使子串满足其中连续重复子串最多. 比如ababab,重复次数为3,ababa,重复次数为1(abab是两次) 恶心在于还要输出最小字典序. 题解网上都有,不发了. 代码: <span style="font-family:KaiTi_GB2312;font-size:1

后缀数组题目整理

最近在跟着 罗穗骞 的论文学习后缀数组, 不亏是神牛的论文.无论是算法讲解,还是习题举例都非常不错.下面把最进做的几道后缀数组整理一下. 1.两字符串的最长公共子串 1 #include <stdio.h> 2 #include <iostream> 3 #include <algorithm> 4 #include <sstream> 5 #include <stdlib.h> 6 #include <string.h> 7 #in

【poj3415】 Common Substrings

http://poj.org/problem?id=3415 (题目链接) 题意 给定两个字符串 A 和 B,求长度不小于 k 的公共子串的个数(可以相同). Solution 后缀数组论文题... 基本思路是计算 A 的所有后缀和 B 的所有后缀之间的最长公共前缀的长度,把最长公共前缀长度不小于 k 的部分全部加起来.先将两个字符串连起来,中间用一个没有出现过的字符隔开.按 height 值分组后,接下来的工作便是快速的统计每组中后缀之间的最长公共前缀之和.扫描一遍,每遇到一个 B 的后缀就统

【uva10829-求形如UVU的串的个数】后缀数组+rmq or 直接for水过

题意:UVU形式的串的个数,V的长度规定,U要一样,位置不同即为不同字串 https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&category=&problem=1770 题解:一开始理解错题意,以为是abcxxxcba(xxx为v),开心地打了后缀数组后发现哎样例不对丫.. UVA的意思是abcxxxabc(xxx为v). 类似poj3693,我们暴