poj1743 Musical Theme
地址:http://poj.org/problem?id=1743
题目:
Musical Theme
Time Limit: 1000MS | Memory Limit: 30000K | |
Total Submissions: 28675 | Accepted: 9674 |
Description
A musical melody is represented as a sequence of N (1<=N<=20000)notes that are integers in the range 1..88, each representing a key on the piano. It is unfortunate but true that this representation of melodies ignores the notion of musical timing; but, this programming task is about notes and not timings.
Many composers structure their music around a repeating &qout;theme&qout;, which, being a subsequence of an entire melody, is a sequence of integers in our representation. A subsequence of a melody is a theme if it:
Transposed means that a constant positive or negative value is added to every note value in the theme subsequence.
Given a melody, compute the length (number of notes) of the longest theme.
One second time limit for this problem's solutions!
Many composers structure their music around a repeating &qout;theme&qout;, which, being a subsequence of an entire melody, is a sequence of integers in our representation. A subsequence of a melody is a theme if it:
- is at least five notes long
- appears (potentially transposed -- see below) again somewhere else in the piece of music
- is disjoint from (i.e., non-overlapping with) at least one of its other appearance(s)
Transposed means that a constant positive or negative value is added to every note value in the theme subsequence.
Given a melody, compute the length (number of notes) of the longest theme.
One second time limit for this problem's solutions!
Input
The input contains several test cases. The first line of each test case contains the integer N. The following n integers represent the sequence of notes.
The last test case is followed by one zero.
The last test case is followed by one zero.
Output
For each test case, the output file should contain a single line with a single integer that represents the length of the longest theme. If there are no themes, output 0.
Sample Input
30
25 27 30 34 39 45 52 60 69 79 69 60 52 45 39 34 30 26 22 18
82 78 74 70 66 67 64 60 65 80
0
Sample Output
5
Hint
Use scanf instead of cin to reduce the read time.
Source
思路:
论文上的题目,相邻数做差后对数组求后缀数组(注意此时差可能为负数,所以加上100保证非负)
然后二分长度,check时把height分组判断即可
补个后缀自动机做法:
先明确几点:
1.每个状态的代表的是endpos集合相同的后缀集合,记max(s)为状态s的endpos集合中长度最长的后缀,min(x)为最小长度的后缀。
则endpos集合中的所有后缀都是max(x)的后缀,这说明endpos是长度为一段连续区间的后缀集合。
2.且endpos(fa[p])是最小的包含endpos(p),且max(fa[p])=min(p)+1。
3.从root出发的所能到达的状态都是子串,且最长不相交子串就在某一状态的endpos集合中。
所以对于每个状态记录endpos集合中的最大值最小值,用l[p],r[p]代表。
拓扑排序后沿反向fa指针dp可以得出每个状态的l[p],r[p]值。
如果r[p]-l[p]>len[fa[p]]则说明endpos(p)中存在不相交的字符串,长度为min(r[p]-l[p],len[p])。
对所有状态扫一遍求出最大值即可。
后缀数组做法:
1 #include <cstdlib>
2 #include <cstring>
3 #include <cstdio>
4 #include <algorithm>
5
6 const int N = 20005;
7 int sa[N],s[N],wa[N], wb[N], ws[N], wv[N];
8 int rank[N], height[N];
9
10 bool cmp(int r[], int a, int b, int l)
11 {
12 return r[a] == r[b] && r[a+l] == r[b+l];
13 }
14
15 void da(int r[], int sa[], int n, int m)
16 {
17 int i, j, p, *x = wa, *y = wb;
18 for (i = 0; i < m; ++i) ws[i] = 0;
19 for (i = 0; i < n; ++i) ws[x[i]=r[i]]++;
20 for (i = 1; i < m; ++i) ws[i] += ws[i-1];
21 for (i = n-1; i >= 0; --i) sa[--ws[x[i]]] = i;
22 for (j = 1, p = 1; p < n; j *= 2, m = p)
23 {
24 for (p = 0, i = n - j; i < n; ++i) y[p++] = i;
25 for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;
26 for (i = 0; i < n; ++i) wv[i] = x[y[i]];
27 for (i = 0; i < m; ++i) ws[i] = 0;
28 for (i = 0; i < n; ++i) ws[wv[i]]++;
29 for (i = 1; i < m; ++i) ws[i] += ws[i-1];
30 for (i = n-1; i >= 0; --i) sa[--ws[wv[i]]] = y[i];
31 for (std::swap(x, y), p = 1, x[sa[0]] = 0, i = 1; i < n; ++i)
32 x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++;
33 }
34 }
35
36 void calheight(int r[], int sa[], int n)
37 {
38 int i, j, k = 0;
39 for (i = 1; i <= n; ++i) rank[sa[i]] = i;
40 for (i = 0; i < n; height[rank[i++]] = k)
41 for (k?k--:0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k++);
42 }
43 bool check(int len,int x)
44 {
45 int mx=sa[1],mi=sa[1];
46 for(int i=2;i<=len;i++)
47 {
48 if(height[i]<x)
49 {
50 if(mx-mi>=x)
51 return 1;
52 mx=mi=sa[i];
53 }
54 else
55 {
56 mx=std::max(sa[i],mx);
57 mi=std::min(sa[i],mi);
58 }
59 }
60 return mx-mi>=x;
61 }
62 int main()
63 {
64 int n;
65 while(scanf("%d",&n)&&n)
66 {
67 int x,ls,len=0;
68 scanf("%d",&ls);
69 for(int i=0;i<n-1;i++)
70 scanf("%d",&x),s[len++]=x-ls+100,ls=x;
71 s[len]=0;
72 da(s,sa,len+1,200);
73 calheight(s,sa,len);
74 int l=5,r=n/2,ans=0;
75 while(l<=r)
76 {
77 int mid=l+r>>1;
78 if(check(len,mid-1))
79 ans=mid,l=mid+1;
80 else
81 r=mid-1;
82 }
83 printf("%d\n",ans);
84 }
85 return 0;
86 }
sam做法:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 using namespace std; 6 7 8 9 struct SAM 10 { 11 static const int MAXN = 20002<<1;//大小为字符串长度两倍 12 static const int LetterSize = 180; 13 14 int tot, last, ch[MAXN][LetterSize], fa[MAXN], len[MAXN]; 15 int sum[MAXN], tp[MAXN], cnt[MAXN]; //sum,tp用于拓扑排序,tp为排序后的数组 16 int l[MAXN],r[MAXN]; 17 void init( void) 18 { 19 last = tot = 1; 20 len[1] = 0; 21 memset(ch,0,sizeof ch); 22 memset(fa,0,sizeof fa); 23 memset(cnt,0,sizeof cnt); 24 memset(l,0x3f3f3f,sizeof l); 25 memset(r,0,sizeof r); 26 } 27 28 void add( int x) 29 { 30 int p = last, np = last = ++tot; 31 l[np] = r[np] = len[np] = len[p] + 1, cnt[last] = 1; 32 while( p && !ch[p][x]) ch[p][x] = np, p = fa[p]; 33 if( p == 0) 34 fa[np] = 1; 35 else 36 { 37 int q = ch[p][x]; 38 if( len[q] == len[p] + 1) 39 fa[np] = q; 40 else 41 { 42 int nq = ++tot; 43 memcpy( ch[nq], ch[q], sizeof ch[q]); 44 len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq; 45 while( p && ch[p][x] == q) ch[p][x] = nq, p = fa[p]; 46 } 47 } 48 } 49 50 void toposort( void) 51 { 52 for(int i = 1; i <= len[last]; i++) sum[i] = 0; 53 for(int i = 1; i <= tot; i++) sum[len[i]]++; 54 for(int i = 1; i <= len[last]; i++) sum[i] += sum[i-1]; 55 for(int i = 1; i <= tot; i++) tp[sum[len[i]]--] = i; 56 for(int i = tot; i; i--) 57 { 58 l[fa[tp[i]]]=min(l[fa[tp[i]]],l[tp[i]]); 59 r[fa[tp[i]]]=max(r[fa[tp[i]]],r[tp[i]]); 60 } 61 int ans=0; 62 for(int i = 1; i <= tot; i++) 63 if(r[i]-l[i]>len[fa[i]]) 64 ans=max(min(r[i]-l[i],len[i]),ans); 65 if(ans<4) ans=-1; 66 printf("%d\n",ans+1); 67 } 68 } sam; 69 70 71 int main(void) 72 { 73 int n,pre; 74 while(scanf("%d",&n)&&n) 75 { 76 sam.init(); 77 scanf("%d",&pre); 78 for(int i=2,now;i<=n;i++) 79 scanf("%d",&now),sam.add(now-pre+87),pre=now; 80 sam.toposort(); 81 } 82 return 0; 83 }
作者:weeping
出处:www.cnblogs.com/weeping/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。