SAM后缀自动机学习小记 Poj 1509 Glass Beads (字符串最小表示)

SAM是给很神奇很难懂的东西,现在能照猫画虎的用一用,还有待深入研究

学习资料

2012年noi冬令营陈立杰讲稿_百度文库

后缀自动机(FHQ+Neroysq补完)_19世纪30年代的空间_百度空间

[转载]后缀自动机学习总结_littlerock_新浪博客

【字符串新武器】后缀自动机 - huyuncong的专栏

字符串模板总结(五):后缀自动机 - bly - 博客频道 - CSDN.NET

【后缀自动机】 - 沐阳 - 博客园


 Poj 1509 Glass Beads

题意:求一个字符串的最小表示法开头字符的位置,如果有多个,求最小的位置

字符串的最小表示有一个专门的优秀算法可以处理:字符串的最小表示法 - whyorwhnt的专栏

用后缀自动机处理也不错。

#include 
#include 
#include 
using namespace std;

const int N = 10005;

class SAM
{
public:
	struct Node {
		int go[26];    //表示当前结点的出边
		int pre, len;    //前继结点 当前结点的最长路径长度
		void init () {
			pre=-1, len=0;
			memset(go, 0xff, sizeof (go));
		}
	};
	Node sn[N<<1];
	int idx, last;   //标号和上一个添加的结点
	void init () {   //初始化SAM
		idx = last = 0;
		sn[idx++].init();
	}
	int newnode () {
		sn[idx].init();
		return idx++;
	}
	void extend (int c) {  //扩展结点
		int end = newnode();
		int tmp = last;    //上一个添加的结点
		sn[end].len = sn[last].len + 1;
		for (; tmp != -1 && sn[tmp].go[c] == -1; tmp = sn[tmp].pre) //对于没有到该字符的祖先结点添加到当前结点的边
			sn[tmp].go[c] = end;
		if (tmp == -1) sn[end].pre = 0; //若该字符第一次出现,添加到根结点的边
		else {
			int nxt = sn[tmp].go[c];
			if (sn[tmp].len + 1 == sn[nxt].len) sn[end].pre = nxt; // 如果可接受点有向c的转移,且长度只加1,那么该孩子可以替代当前的end,并且end的双亲指向该孩子 
			else {
				int np = newnode();   //新建结点并将nxt信息复制
				sn[np] = sn[nxt];
				sn[np].len = sn[tmp].len + 1;
				sn[end].pre = sn[nxt].pre = np; //设置np为end和nxt的前继节点
				for (; tmp != -1 && sn[tmp].go[c] == nxt; tmp = sn[tmp].pre) //将指向nxt的tmp的祖先结点改为指向np
                    sn[tmp].go[c] = np;
			}
		}
		last = end;
	}
	void build (char str[],int low)
	{//传入参数:字符串和字符串中最小的字符
		init();
		int len=strlen(str);
		for (int i=0;i


最小表示的代码:

#include 
#include 
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))

const int N = 10005;

int minstr(char s[])
{
	int len=strlen(s);
	int i=0,j=1;
	while(i<=len-1 && j<=len-1)
	{
		int k=0;
		while(k<=len-1 && s[(i+k)%len]==s[(j+k)%len])
			k++;
		if(k>=len)
			break;
		if(s[(i+k)%len]>s[(j+k)%len])
			i=max(i+k+1,j+1);//此时s[i+1]到s[i+k]都不可能是最小字符串的开头,而且s[i+1]到s[j]也不可能是最小字符串的开头(s[i]开头的字符串与s[j]开头的字符串在比较,本身已经说明s[i]开头的字符串小于以s[i+1]开头到以s[j-1]开头的字符串了)
		else
			j=max(j+k+1,i+1);//同上道理
	}
	return min(i,j);
}

char str[N];

int main ()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%s", str);
		printf("%d\n",minstr(str)+1);
	}
	return 0;
}





你可能感兴趣的:(KMP,exKMP,字符串,学习轨迹,后缀数组)