• @poj



    @description@

    给定一个由小写字母构成的圆环形的字符串(即首字母和末字母是相连的)。
    让你找到一个位置将这个圆环形的串断开成为一个序列形的串,使得这个字符串字典序最小。如果有多个,输出位置最靠前的那一个。

    input
    多组数据。第一行输入数据组数 N。
    接下来 N 行,每行一个长度不超过 10000 个字符的字符串。保证仅由小写字母组成。

    output
    对于每组数据,输出一个位置。表示以这个位子作为字符串起点字符串的字典序最小。

    sample input
    4
    helloworld
    amandamanda
    dontcallmebfu
    aaabaaa
    sample output
    10
    11
    6
    5

    @solution@

    据说这道题可以用什么……最小表示法来做。不过我觉得反正后缀自动机也不难写,就直接用后缀自动机吧。

    圆环嘛,我们就把原字符串 S 后面再拼一个 S ,然后求 SS 长度为 |S| 且字典序最小的串嘛。
    后缀自动机中每一个结点都可以表示若干子串。
    那我从起始结点出发,每次总是沿着字典序最小的边走,走 |S| 步不就可以了嘛。

    它还要求位置最靠前。后缀自动机里面跟位置有关的,就是 end-pos 嘛。那我就是 end-pos 集合里面最小的那个再减去字符串长度就可以了嘛。

    跟求解子串出现次数一样,我们把每次通过增量法增加的结点视为有效结点,再通过桶排序向 fa 转移,转移时取最小值即可。

    @accepted code@

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int INF = (1<<30);
    const int MAXN = 10000;
    struct sam{
    	sam *fa, *ch[26]; int mx;
    	sam *nxt; int mnps;
    }pl[4*MAXN + 5], *bin[2*MAXN + 5], *tcnt, *root, *lst;
    void init() {
    	tcnt = root = lst = &pl[0];
    	for(int i=0;i<26;i++)
    		root->ch[i] = NULL;
    	root->nxt = root->fa = NULL;
    	root->mx = 0, root->mnps = INF;
    }
    sam *newnode(int x = INF) {
    	tcnt++;
    	for(int i=0;i<26;i++)
    		tcnt->ch[i] = NULL;
    	tcnt->nxt = tcnt->fa = NULL;
    	tcnt->mx = 0, tcnt->mnps = x;
    	return tcnt;
    }
    void add_bin(sam *x) {
    	x->nxt = bin[x->mx];
    	bin[x->mx] = x;
    }
    char s[MAXN + 5];
    void clone(sam *x, sam *y) {
    	for(int i=0;i<26;i++)
    		x->ch[i] = y->ch[i];
    	x->fa = y->fa;
    }
    void sam_extend(int pos, int x) {
    	sam *cur = newnode(pos), *p = lst;
    	cur->mx = lst->mx + 1; lst = cur;
    	add_bin(cur);
    	while( p && !p->ch[x] )
    		p->ch[x] = cur, p = p->fa;
    	if( !p )
    		cur->fa = root;
    	else {
    		sam *q = p->ch[x];
    		if( p->mx + 1 == q->mx )
    			cur->fa = q;
    		else {
    			sam *nq = newnode();
    			clone(nq, q);
    			nq->mx = p->mx + 1;
    			add_bin(nq);
    			cur->fa = q->fa = nq;
    			while( p && p->ch[x] == q )
    				p->ch[x] = nq, p = p->fa;
    		}
    	}
    }
    void solve() {
    	init(); scanf("%s", s);
    	int lens = strlen(s);
    	for(int i=0;i<lens;i++)
    		sam_extend(i, s[i]-'a');
    	for(int i=0;i<lens;i++)
    		sam_extend(lens+i, s[i]-'a');
    	for(int i=2*lens;i>=1;i--)
    		while( bin[i] ) {
    			bin[i]->fa->mnps = min(bin[i]->fa->mnps, bin[i]->mnps);
    			bin[i] = bin[i]->nxt;
    		}
    	sam *nw = root;
    	for(int i=0;i<lens;i++) {
    		for(int j=0;j<26;j++)
    			if( nw->ch[j] ) {
    				nw = nw->ch[j];
    				break;
    			}
    	}
    	printf("%d
    ", (nw->mnps + 1)%lens + 1);
    }
    int main() {
    	int N; scanf("%d", &N);
    	for(int i=1;i<=N;i++) solve();
    }
    

    @details@

    注意因为字符串扩展成了两倍,所以数组要相应的开成两倍大。否则会 RE。

  • 相关阅读:
    男人要知道的40条忠告
    利用xtraBackup实现不停master服务做主从同步
    MY SQL 知识
    房价与阶级
    SQL Server数据库级别触发器
    mysql 5.7开启并行复制
    SQL Server 查出未提交事务(长事务)SQL
    开源数据集
    telnet
    Zend Studio使用综述
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10252268.html
Copyright © 2020-2023  润新知