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: 
  • 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. 

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 }

 

posted @ 2017-03-30 21:42  weeping  阅读(198)  评论(0编辑  收藏  举报