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

4 篇文章 0 订阅
1 篇文章 0 订阅

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


提示: ( 如果你是顺着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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值