UVALive

  题意:找一个出现了m次的最长子串,以及这时的最右的位置。

  hash的话代码还是比较好写的,,但是时间比SA多很多。。

 1 #include <stdio.h>
 2 #include <algorithm>
 3 #include <string.h>
 4 using namespace std;
 5 const int N = 40000 + 100;
 6 typedef long long ll;
 7 const int X = 257;
 8 const int mod = (int)1e9 + 7;
 9 
10 char s[N];
11 int m,len,pw[N];
12 int H[N],pos;
13 
14 struct node
15 {
16     int id,hash;
17     bool operator < (const node & temp) const
18     {
19         return hash == temp.hash ? id < temp.id : hash < temp.hash;
20     }
21 }p[N];
22 
23 bool solve(int L)
24 {
25     int cnt = 0;
26     pos = -1;
27     for(int i=1;i+L-1<=len;i++)
28     {
29         int id = i;
30         int hash = ((ll)H[i] - (ll)H[i+L]*pw[L]) % mod;
31         if(hash < 0) hash += mod; // 注意这里!
32         p[i] = (node){id, hash};
33     }
34     sort(p+1, p+1+ len - L + 1);
35 
36     for(int i=1;i<=len-L+1;i++)
37     {
38         if(i == 1 || p[i].hash != p[i-1].hash) cnt = 0;
39         if(++cnt >= m) pos = max(pos, p[i].id);
40     }
41     return pos != -1;
42 }
43 
44 int main()
45 {
46     pw[0] = 1;
47     for(int i=1;i<N;i++) pw[i] = 1LL*pw[i-1] * X % mod;
48     while(scanf("%d",&m) == 1 && m)
49     {
50         scanf("%s",s+1);
51         len = strlen(s+1);
52         for(int i=len;i>=1;i--) H[i] = (1LL*H[i+1] * X + s[i] - 'a') % mod;
53         int l = 1, r = len, ans = -1;
54         while(l <= r)
55         {
56             int mid = l + r >> 1;
57             if(solve(mid)) ans = mid, l = mid + 1;
58             else r = mid - 1;
59         }
60         solve(ans);
61         if(ans != -1) printf("%d %d
",ans, pos-1);
62         else puts("none");
63     }
64     return 0;
65 }
hash的写法

  

  SA的话,写法需要细细体会一下了。。时间减少了很多。

  1 #include <stdio.h>
  2 #include <algorithm>
  3 #include <string.h>
  4 using namespace std;
  5 const int N = 40000 + 100;
  6 typedef long long ll;
  7 
  8 /**
  9  *    sa[i]:表示排在第i位的后缀的起始下标
 10  *    rank[i]:表示后缀suffix(i)排在第几
 11  *    height[i]:sa[i-1] 与 sa[i]的LCP(最长公共前缀)值
 12  *
 13  * */
 14  /*
 15     如果整数的话模板改成int.
 16     加一个数a[n] = 0 。 这样他的排名是第一个。
 17     construct(a,n+1);
 18 
 19     字符串的话。
 20     len = strlen(str);
 21     construct(s,strlen(s)+1);
 22     排名第0的是个空字符串。
 23 
 24     height[i]:sa[i-1] 与 sa[i]的LCP(最长公共前缀)值
 25     所以height[1] = 0;
 26     rank[len] = 0;
 27     sa[0] = len;
 28  */
 29 int sa[N],rnk[N],height[N];
 30 void construct(const char *s,int n,int m = 256) {
 31     static int t1[N],t2[N],c[N];
 32     int *x = t1,*y = t2;
 33     int i,j,k,p,l;
 34     for (i = 0; i < m; ++ i) c[i] = 0;
 35     for (i = 0; i < n; ++ i) c[x[i] = s[i]] ++;
 36     for (i = 1; i < m; ++ i) c[i] += c[i - 1];
 37     for (i = n - 1; i >= 0; -- i) sa[--c[x[i]]] = i;
 38     for (k = 1; k <= n; k <<= 1) {
 39         p = 0;
 40         for (i = n - k; i < n; ++ i) y[p++] = i;
 41         for (i = 0; i < n; ++ i) if (sa[i] >= k) y[p++] = sa[i] - k;
 42         for (i = 0; i < m; ++ i) c[i] = 0;
 43         for (i = 0; i < n; ++ i) c[x[y[i]]] ++;
 44         for (i = 1; i < m; ++ i) c[i] += c[i - 1];
 45         for (i = n - 1; i >= 0; -- i) sa[--c[x[y[i]]]] = y[i];
 46         std::swap(x,y);
 47         p = 1; x[sa[0]] = 0;
 48         for (i = 1; i < n; ++ i)
 49             x[sa[i]] = y[sa[i - 1]] == y[sa[i]]
 50                 && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1: p ++;
 51         if (p >= n) break;
 52         m = p;
 53     }
 54     for (i = 0; i < n; ++ i) rnk[sa[i]] = i;
 55     for (i = 0,l = 0; i < n; ++ i) {
 56         if (rnk[i]) {
 57             j = sa[rnk[i] - 1];
 58             while (s[i + l] == s[j + l]) l++;
 59             height[rnk[i]] = l;
 60             if (l) l--;
 61         }
 62     }
 63 }
 64 
 65 int m,pos,len;
 66 char s[N];
 67 bool solve(int L)
 68 {
 69     // 用后缀数组的话,如果重复长度就是最长的长度,需要特判
 70     if(m == 1 && L == len) {pos = 0; return 1;}
 71     pos = -1;
 72     int cnt = 1;
 73     int temp = -1; // 要用一个temp来记录当前cnt>=m的最大的位置
 74     for(int i=1;i<=len;i++)
 75     {
 76         if(height[i] >= L) cnt++, temp = max(temp, sa[i]);
 77         else cnt = 1, temp = sa[i];
 78         if(cnt >= m) pos = max(pos, temp);
 79     }
 80     return pos != -1;
 81 }
 82 
 83 int main()
 84 {
 85     while(scanf("%d",&m) == 1 && m)
 86     {
 87         scanf("%s",s);
 88         len = strlen(s);
 89         construct(s, len+1);
 90         int l = 1, r = len;
 91         int ans = -1;
 92         while(l <= r)
 93         {
 94             int mid = l + r >> 1;
 95             if(solve(mid)) ans = mid, l = mid + 1;
 96             else r = mid - 1;
 97         }
 98         solve(ans);
 99         if(ans != -1) printf("%d %d
",r,pos);
100         else puts("none");
101     }
102     return 0;
103 }
SA的写法

  顺便想说一下的是,这里不知为何下标从1开始无限WA,以后用SA还是下标从0开始好了。。毕竟不懂SA的具体原理。

原文地址:https://www.cnblogs.com/zzyDS/p/6189539.html