hdu5782 Cycle(后缀数组+bitset)(16多校赛5)

2 篇文章 0 订阅
1 篇文章 0 订阅

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 235    Accepted Submission(s): 75


Problem Description
Alice get two strings and the lengths are both N. Bored Alice wanna know whether all equal length prefix string of these two strings are CycleEqual. For example, "abc" and "bca" and "cab" are CycleEqual. So you need output N character, '0' is no, '1' is yes.
 

Input
The input contains multiple test cases.

For each test case, the first contains one string and the second line contains one string. The lengths of strings are both  N(1N10000) .
 

Output
For each test case, output N characters. For ith character, if prefix strings of first i character are CycleEqual, output '1', else output '0'.
 

Sample Input
  
  
abc cab aa aa
 

Sample Output
  
  
001 11
 

Author
ZSTU
 

Source



题目大意:
        给出两个字符串a,b,对于ab每一个等长的前缀,求解他们是不是互为循环串(首尾相接后通过旋转可以得到另一个串就是循环串)
解题思路:
        首先考虑循环串有一个性质:a串可以拆成一个前缀和一个后缀,交换下位置就得到了B串
        那么对于每一个i记录一个len,表示a串i~n和b串0~n的最长公共前缀的长度。显然这b串0~0,0~1,...,0~len-1的这些前缀串和a串i~i,....i~i+len-1这些子串是相等的
        所以如果b串的那些前缀串之后紧跟着的i个字符所组成的子串和a的0~i-1相等的话,那就组成了一个循环串。用bs[i](是一个bitset)表示a串0~i和哪些b的k~k+i串相等,如果相等,bs[i][k]=1;
     相等的那些就是所有有可能放在b的那些前缀后面形成循环串的方案。通过掩码和位运算的操作可以快速得到答案。
     
    然后就是len的求法,后缀数组+rmq即可,不再赘述,bs的求法是通过另一个数组vt[i]表示a串0~n,b串i~n的最长公共前缀的长度,实质上就是len的求解翻过来,求法后缀数组+rmq。
    有了vt[i],对vt[i]从大到小排序(下标需要记录),对于bs,从bs[n-1]倒序的求解,记录游标cr=0。如果vt[cr]>=n-1,那么这个前缀一定可以包含bs[n-1]合法的k的位置,记录到一个bitset里,cr++,处理出所有bs。
    为什么要从大到小求bs呢??因为vt数组前缀的长度是递减的,更长前缀之前满足条件的k,在更短前缀也满足,所以实际上在求的bitset是逐步增多的,并且是包含的关系。
    
    有了bs,有了len,那么答案需要找出bs中满足条件的k,条件就是i<=k+i-1<=i+len-1,所以掩码就是除了i~i+len-1位都是1,其他位均为0,用一个pre[i]bitset表示0~i为1其他位0的串,那么所需的就是pre[i+len-1]^pre[i-1]。
 

#include<stdio.h>
#include<string.h>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
const int N = 10000 + 5;
int wa[N << 1], wb[N << 1], wc[N << 1], wv[N << 1];
bool cmp(int *r, int a, int b, int l) {
	return r[a] == r[b] && r[a + l] == r[b + l];
}
void da(int *r, int *sa, int n, int m) {
	int *x = wa, *y = wb;
	for (int i = 0; i < m; i++) wc[i] = 0;
	for (int i = 0; i < n; i++) wc[x[i] = r[i]]++;
	for (int i = 1; i < m; i++) wc[i] += wc[i - 1];
	for (int i = n - 1; i >= 0; i--) sa[--wc[x[i]]] = i;
	for (int j = 1, p = 1; p < n; j <<= 1, m = p) {
		p = 0;
		for (int i = n - j; i < n; i++) y[p++] = i;
		for (int i = 0; i < n; i++) if (sa[i] >= j) y[p++] = sa[i] - j;
		for (int i = 0; i < n; i++) wv[i] = x[y[i]];
		for (int i = 0; i < m; i++) wc[i] = 0;
		for (int i = 0; i < n; i++) wc[wv[i]]++;
		for (int i = 1; i < m; i++) wc[i] += wc[i - 1];
		for (int i = n - 1; i >= 0; i--) sa[--wc[wv[i]]] = y[i];
		int *t = x; x = y; y = t;
		p = 1; x[sa[0]] = 0;
		for (int i = 1; i < n; i++)
			x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++;
	}
}
void calheight(int *r, int *sa, int *rk, int *height, int n) {
	for (int i = 1; i <= n; i++) rk[sa[i]] = i;
	int k = 0;
	for (int i = 0; i < n; height[rk[i++]] = k) {
		if (k) k--;
		for (int j = sa[rk[i] - 1]; r[i + k] == r[j + k]; k++);
	}
}
int Log[N << 1], rq[N << 1][20];

void build_rmq(int *a, int n) {
	for (int i = 1; i <= n; i++)  rq[i][0] = a[i];
	for (int j = 1; j < 20; j++) {
		for (int i = 1; i + (1 << j) - 1 <= n; i++)
			rq[i][j] = min(rq[i][j - 1], rq[i + (1 << j - 1)][j - 1]);
	}
	int cr = 0;
	for (int i = 1; i <= n; i++) {
		while ((1 << cr + 1) <= i) cr++;
		Log[i] = cr;
	}
}
int query_rmq(int l, int r) {
	int k = Log[r - l + 1];
	return min(rq[l][k], rq[r - (1 << k) + 1][k]);
}

int a[N << 1], sa[N << 1], rk[N << 1], height[N << 1];

char s1[N], s2[N];
bitset<10000> ans, bs[N], pre[N];
pair<int, int> vt[N];

void solve() {
	int n = strlen(s1);
	int tot = 0;
	for (int i = 0; i < n; i++) a[tot++] = s1[i] - 'a' + 1;
	a[tot++] = 27;
	for (int i = 0; i < n; i++) a[tot++] = s2[i] - 'a' + 1;
	a[tot++] = 0;
	da(a, sa, tot, 28);
	calheight(a, sa, rk, height, tot - 1);
	build_rmq(height, tot - 1);

	for (int i = 0; i < n; i++) {
		int l = rk[n + 1 + i], r = rk[0];
		if (l > r) swap(l, r);
		int len = query_rmq(l + 1, r);
		vt[i] = { -len, i };
	}
	sort(vt, vt + n);
	bitset<10000> tmp;
	int cr = 0;
	for (int i = n - 1; i >= 0; i--) {
		while (cr<n && -vt[cr].first >= i + 1) {
			tmp[vt[cr].second] = 1;
			cr++;
		}
		bs[i] = tmp;
	}
	ans.reset();
	for (int i = 0; i < n; i++) {
		int l = rk[i], r = rk[n + 1];
		if (l > r) swap(l, r);
		int len = query_rmq(l + 1, r);
		if (len > 0) {
			if (i > 0)
				ans |= (pre[i + len - 1] ^ pre[i - 1])&(bs[i - 1] << (i - 1));
			else
				ans |= pre[i + len - 1];
		}
	}
	for (int i = 0; i < n; i++) {
		if (ans.test(i)) printf("1");
		else    printf("0");
	}
	puts("");
}

void init() {
	pre[0][0] = 1;
	for (int i = 1; i <= 10000; i++) {
		pre[i] = pre[i - 1];
		pre[i][i] = 1;
	}
}

int main() {
	init();
	while (scanf("%s%s", s1, s2) == 2) {
		solve();
	}
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值