UVa 719 - Glass Beads 字符串算法的合集

来源:互联网 发布:网络视频会议软件 免费 编辑:程序博客网 时间:2024/05/21 15:48

本文将用三个算法来解决此题


提示: ( 如果你是顺着INTERMEDIATE往下做的这个题有一个明显的思路)

1. 想办法使用后缀数组 , 因为题目要求的是一种字符串的循环表示形式(AKA 字符串表示法) , 那么可以把字符串写两遍然后跑一跑后缀数组啊

2. 跑出来后SA[1]不一定是答案 , 因为还有可能是后面那个加上字符串的某个后缀 , 所以要找第一个SA小于原来字符串长度的后缀.......

3. 还没完 , 找到后还需要往后一直找 , 找到一个SA最小并且中途的HEIGHT不小于字符串长度的下标....(有点废话是吧)


实现后的代码如下(等会贴出来)

#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <string>#include <vector>#include <deque>#include <stack>#include <algorithm>using namespace std;const int maxn = 2e4+1e2;int s[maxn] , n;int t1[maxn] , t2[maxn] , c[maxn] , sa[maxn] , height[maxn] , Rank[maxn];void SA(int m){int *x = t1 , *y = t2;for(int i=0;i<m;i++) c[i] = 0;for(int i=0;i<n;i++)  c[x[i] = s[i]] ++;for(int i=1;i<m;i++) c[i] += c[i-1];for(int i=0;i<n;i++) sa[--c[x[i]]] = i;for(int k=1;k<=n;k*=2){int p = 0;for(int i=n-k;i<n;i++) y[p++] = i;for(int i=0;i<n;i++) if(sa[i]>=k) y[p++] = sa[i]-k;for(int i=0;i<m;i++) c[i] = 0;for(int i=0;i<n;i++) c[x[i]] ++;for(int i=1;i<m;i++) c[i] += c[i-1];for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]] = y[i];swap(x , y);p=1;x[sa[0]] = 0;for(int i=1;i<n;i++) x[sa[i]] = y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k] ? p-1 : p++;if(p>=n) break;m = p;}}void getHeight(){for(int i=0;i<n;i++) Rank[sa[i]] = i;int k = 0 , j;for(int i=0;i<n-1;i++){if(k) k--;j = sa[Rank[i]-1];while(s[i+k] == s[j+k]) k++;height[Rank[i]] = k;}}int id(char c) { return c-'a'+1; }char ss[maxn];int main(int argc, char *argv[]) {int t;scanf("%d",&t);while(t--){scanf("%s",ss);int len = strlen(ss);n = 0;for(int i=0;i<len;i++) s[n++] = id(ss[i]);for(int i=0;i<len;i++) s[n++] = id(ss[i]);s[n++] = 0;SA(28);getHeight();for(int i=0;i<n;i++) if(sa[i]<len){int res = sa[i];while(height[i+1]>=len && i+1<n) { i++; if(sa[i]<len) res = min(res , sa[i]); }cout<<res+1<<endl; break; }}return 0;}



第二个思路 , 后缀自动机...........如果不看不慌不忙的博客我也不知道这个数据结构 , 研究了陈姐姐(CLJ)的论文(自行百度)。 然后顺着自动机找一条最小路径就OK了

#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <vector>#include <deque>#include <algorithm>#include <stack>#include <string>#include <map>#include <set>using namespace std;const int sigmaSize = 26;const int maxn = 1e4+1e2;struct state{state* go[sigmaSize] , *pre;int Max;state(int _Max = 0): pre(0), Max(_Max) {   memset(go , 0 , sizeof(go));  }};int sz;state pool[maxn*3];inline state* newnode(int _Max = 0) { memset(pool[sz].go , 0 , sizeof(pool[sz].go)); pool[sz].pre = 0; pool[sz].Max = _Max; return &pool[sz++]; }state *root , *last;void extend(int w){state* p = last;state* np = newnode(p->Max+1);while(p && p->go[w] ==0)p->go[w] = np , p = p->pre;if(p){state* q = p->go[w];if(p->Max+1 == q->Max)np->pre = q;else {state* nq = newnode(p->Max+1);memcpy(nq->go , q->go , sizeof q->go );nq->pre = q->pre;q->pre = nq;np->pre = nq;while(p && p->go[w] == q) p->go[w] = nq , p = p->pre;}}else np->pre = root;last = np;}int query(int n){state* now = root;while(n--)for(int i=0;i<sigmaSize;i++) if(now->go[i]) { now = now->go[i]; break; }return now->Max;}inline int id(char c) { return c-'a'; }char s[maxn];int main(){freopen("in","r",stdin);int t , n;scanf("%d" , &t);while(t-- && scanf("%s",s)){sz = 0;root = newnode();last = root;n = strlen(s);for(int k=0;k<2;k++) for(int i=0;i<n;i++) extend(id(s[i]));printf("%d\n",query(n)-n+1);}return 0;}

第三个思路是我测试时间表现最好的算法 , 基于周源的字符串的最小表示法 , 是几乎没有常数的 , 但这个方法对于处理字符串的其他问题是有局限性的


代码如下:(等会贴出)

#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <string>#include <vector>#include <deque>#include <stack>#include <algorithm>using namespace std;const int maxn = 1e4+1e2;char s[maxn];int main(int argc, char *argv[]) {int t;scanf("%d",&t);while(t--){scanf("%s",s);int i =0  ,  j = 0 , n = (int)strlen(s) , k;while(i<n && j<n){if(i==j) j++;k = 0;while(k<=n && s[(i+k)%n]==s[(j+k)%n]) k++;if(s[(i+k)%n]>s[(j+k)%n]) i+=k+1;else j+=k+1;}printf("%d\n",i+1); }return 0;}



时间从上到下依次变快 , 可以自行比较



2 0
原创粉丝点击