zoukankan      html  css  js  c++  java
  • 【ybt金牌导航2-2-3】【POJ 3693】连续重复子串 / Maximum repetition substring

    连续重复子串 / Maximum repetition substring

    题目链接:ybt金牌导航2-2-3 / POJ 3693

    题目大意

    给你一个字符串,要你找一个子串,让它可以由另一个子串复制 x 次得到,而且这个 x 是最大的那个。
    要你输出这个子串。

    思路

    首先你看到复制多次得到,想到可以用 LCP 来求。
    什么意思呢?画个图就清楚了。
    在这里插入图片描述
    像这样等量代换一下,就可以说明它是复制多次得到了。

    那你可以看出,你可以枚举两个的左边 (x,y),然后得到的 (LCP)(z),那个数就是 ((y-x)/z+1)

    (LCP) 要求两两之间容易想到用 SA,那么接着的问题就是枚举 (x,y) 也会超时。

    那我们考虑怎么搞。
    发现枚举两左边不知道怎么搞,我们考虑先枚举两个左边相差的长度(也就是要复制的字符串一份的长度),然后再枚举一个左边的。
    然后似乎还是没有什么想法对吧。
    那我们想到,你确定了一个的长度 (L),那就说明,你最终根据它选到的子串一定会包含一些位置的其中一个。没错,就是 (s_0,s_L,s_{2L},...)
    那你就可以枚举这些点,然后从它们开始找。

    然后这时候你会发现中间的也可以做左边,那你求完长度之后,你再往左也延伸。
    (或者说看从更左的地方会不会更优)

    然后最后记录下最多循环次数的长度,然后根据长度求字典序最小的就好了。
    求字典序最小就直接按后缀数组枚举,然后枚举记录的长度看是不是符合,符合就输出。

    代码

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    
    char s[300001];
    int n, xx[300001], yy[300001], tong[300001];
    int fir[300001], kind, ynum, height[300001][22];
    int rank[300001], size[500001], max_time;
    int final_st, final_L, tmp, sa[300001], r[300001];
    
    void sort_(int m, int *x, int *y) {//基数排序
    	for (int i = 1; i <= m; i++)
    		tong[i] = 0;
    	for (int i = 1; i <= n; i++)
    		tong[x[i]]++;
    	for (int i = 2; i <= m; i++)
    		tong[i] += tong[i - 1];
    	for (int i = n; i >= 1; i--)
    		sa[tong[fir[i]]--] = y[i];
    }
    
    void SA(int *r, int *sa, int n, int m) {//倍增求SA数组
    	int *x = xx, *y = yy;
    	for (int i = 1; i <= n; i++)
    		x[i] = r[i];
    	for (int i = 1; i <= n; i++)
    		y[i] = i;
    	for (int i = 1; i <= n; i++)
    		fir[i] = x[y[i]];
    	sort_(m, x, y);
    	
    	for (int j = 1; j < n; j <<= 1) {
    		ynum = 0;
    		for (int i = n - j + 1; i <= n; i++)
    			y[++ynum] = i;
    		for (int i = 1; i <= n; i++)
    			if (sa[i] > j) y[++ynum] = sa[i] - j;
    		for (int i = 1; i <= n; i++)
    			fir[i] = x[y[i]];
    		sort_(m, x, y);
    		
    		swap(x, y);
    		kind = 1;
    		x[sa[1]] = 1;
    		for (int i = 2; i <= n; i++)
    			if (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + j] == y[sa[i - 1] + j])
    				x[sa[i]] = kind;
    			else x[sa[i]] = ++kind;
    		
    		if (kind == n) break;
    		m = kind;
    	}
    }
    
    void get_height(int *r, int *sa, int n) {//求height数组
    	int k = 0, j;
    	for (int i = 1; i <= n; i++) rank[sa[i]] = i;
    	for (int i = 1; i <= n; i++) {
    		if (k) k--;
    		j = sa[rank[i] - 1];
    		while (r[i + k] == r[j + k] && i + k <= n && j + k <= n) k++;
    		height[rank[i]][0] = k;
    	}
    }
    
    int query(int x, int y) {//ST表查询LCP
    	if (x > y) swap(x, y);
    	x++;
    	int half = 0;
    	while (x + (1 << (half + 1)) <= y) half++;
    	return min(height[x][half], height[y - (1 << half) + 1][half]);
    }
    
    int main() {
    	scanf("%s", s + 1);
    	n = strlen(s + 1);
    	while (n != 1 || s[1] != '#') {
    		max_time = 0;
    		size[0] = 0;
    		for (int i = 1; i <= n; i++)
    			r[i] = s[i];
    		
    		SA(r, sa, n, 300);
    		
    		get_height(r, sa, n);
    		
    		for (int i = 1; i <= 20; i++)//ST表预处理LCP
    			for (int j = 1; j <= n && j + (1 << i) - 1 <= n; j++)
    				height[j][i] = min(height[j][i - 1], height[j + (1 << (i - 1))][i - 1]);
    		
    		for (int L = 1; L <= n; L++) {//枚举长度
    			for (int j = 1; j + L <= n; j += L) {//枚举必会碰的位置
    				int LCP_ = query(rank[j], rank[j + L]);
    				int re = LCP_ / L + 1;
    				
    				int bef = j - (L - (LCP_ % L));//可能前面一段也有
    				if (bef >= 0 && LCP_ % L != 0 && query(rank[bef], rank[bef + L]) >= j - bef)
    					re++;
    				
    				if (re > max_time) {//取最优
    					max_time = re;
    					size[0] = 0;
    					size[++size[0]] = L;
    				}
    				else if (re == max_time) {
    					size[++size[0]] = L;
    				}
    			}
    		}
    		
    		final_st = 0;//找字典序最小的
    		for (int i = 1; i <= n; i++) {//记得这里枚举的不是字符串下标,而是后缀排名
    			for (int j = 1; j <= size[0]; j++)
    				if (query(rank[sa[i]], rank[sa[i] + size[j]]) >= (max_time - 1) * size[j]) {
    					final_st = sa[i];
    					final_L = size[j];
    					break;
    				}
    			if (final_st) break;
    		}
    		
    		printf("Case %d: ", ++tmp);
    		for (int i = final_st; i <= final_st + max_time * final_L - 1; i++)
    			printf("%c", s[i]);
    		printf("
    ");
    		
    		scanf("%s", s + 1);
    		n = strlen(s + 1);
    	}
    	
    	return 0;
    }
    
    
  • 相关阅读:
    里氏代换原则
    依赖倒转原则
    开放-封闭原则
    如何判断对象是否死亡和类是无用的类
    Java内存区域
    Zookeeper使用场景
    zookeeper知识点总结
    前端小技术总结
    lambda表达式的使用
    Comparator进行List集合排序
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_2-2-3.html
Copyright © 2011-2022 走看看