fjnu校赛F题(字符串_后缀数组)

题目链接:http://acm.fafu.edu.cn/problem.php?id=1241


题目描述:

  Welcome to Fjnu and don’t miss our fantasy lottery! In every period,there will be a given string and one of its substring is considered “Lucky”.Every student has a change to join in it. If you choose the right one, it willbe a great honor in Fjnu. Alice and Bob are all Fjnu’s Acmers, both of themwant to pick up the right substring.

  Alice is a clever girl, she will choose the most probably one.

  Bob is a lazy boy, he will choose a substring randomly.

  Now your job is to calculate the Alice’s and Bob’s winning probability.

 

Input

The first line of date is an integer T, which is the number of thetext cases.

Then T cases follow, each case contains a string of one line.

 

Output

For each case, you should output the Alice’s and Bob’s winning probability, accurateup to 10 decimal places

 

SimpleInput

4

a

aa

helloeveryone

welcometofjnu

 

SimpleOutput

Case 1: Alice:1.0000000000 Bob:1.0000000000

Case 2: Alice:0.6666666667 Bob:0.5555555556

Case 3: Alice:0.0439560440 Bob:0.0129211448

Case 4: Alice:0.0219780220 Bob:0.0114720444

 

数据范围:

T<=100.

The length of the string<=10000.

The string only contains lowercase letters.


解题思路:

    Alice的概率好求,只要找出现最多的字符数量除以子串总数就可以,单个字符出现的概率肯定最大。

    Bob的选择策略是随机选择一个,然后和最后的结果匹配,由于两个都是随机,可以设想他们为两个串,然后就很好理解了。把两个串拼接起来,中间用神奇地‘$'字符隔开。然后用倍增算法求sa数组、rank数组、height数组,那现在问题转化求为前一个串中的子串和后一个子串相相等的总匹配数,这个总数除以子串总数的平方就ok了。

   上面的逻辑过程较容易理解,用aa就能推出为什么答案是5/9即0.05555555556.关键是要找出两个串子串相等的总匹配数。求总匹配数可用单调栈求解。


代码:

#include <stdio.h>
#include <string.h>
#define MAX 20010


int n,len,tot,top,len1;
int st[MAX],num[MAX],arr[MAX];
double ans1,ans2;
int wa[MAX],wb[MAX];
int wv[MAX],wn[MAX];
int sa[MAX],rank[MAX],h[MAX];


int 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 n,int m) {
	
	int i,j,k,p,*t;
	int *x = wa,*y = wb;
	
	
	for (i = 0; i < m; ++i) wn[i] = 0;
	for (i = 0; i < n; ++i) wn[x[i]=r[i]]++;
	for (i = 1; i < m; ++i) wn[i] += wn[i-1];
	for (i = n - 1; i >= 0; --i) sa[--wn[x[i]]] = i;
	
	
	for (j = 1,p = 1; p < n; j *= 2,m = p) {
		
		for (p = 0,i = n - j; i < n; ++i) y[p++] = i;
		for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;
		
		
		for (i = 0; i < n; ++i) wv[i] = x[y[i]];
		for (i = 0; i < m; ++i) wn[i] = 0;
		for (i = 0; i < n; ++i) wn[wv[i]]++;
		for (i = 1; i < m; ++i) wn[i] += wn[i-1];
		for (i = n - 1; i >= 0; --i) sa[--wn[wv[i]]] = y[i];
		
		
		t = x,x = y,y = t,p = 1;
		for (x[sa[0]] = 0,i = 1; i < n; ++i)
			x[sa[i]] = cmp(y,sa[i-1],sa[i],j) ? p - 1 : p++;
	}
}
void CalHeight(int *r,int n) {
	
	int i,j,k = 0;
	for (i = 1; i <= n; ++i) rank[sa[i]] = i;
	for (i = 0; i < n; h[rank[i++]] = k)
		for (k ? k-- : 0,j = sa[rank[i]-1]; r[i+k] == r[j+k];k++);
}


int Solve(int n) {
	
	int i,j,tp,ans = 0,k = 1;
	for (i = 1; i <= n; ++i) {				//单调栈处理
		
		if (h[i] < k) tot = top = 0;		//分组,小于k的就分为一组,不多做处理
		else {

			tp = 0;							//默认tp = 0
			if (sa[i-1] > len1)				//如果前面一段是字符串B
				tp = 1,tot += h[i] - k + 1; //tp是累计可增加的贡献值

	
			while (top > 0 && st[top] >= h[i]) {

				tot -= num[top] * (st[top] - h[i]);
				tp += num[top],top--;
			}


			st[++top] = h[i],num[top] = tp;
			if (sa[i] < len1) ans += tot;
		}
	}
	

	for (i = 1; i <= n; ++i) { //单调栈处理
		
		if (h[i] < k) tot = top = 0;
		else {

			tp = 0;
			if (sa[i-1] < len1)
				tp = 1,tot += h[i] - k + 1;

	
			while (top > 0 && st[top] >= h[i]) {
				
				tot -= num[top] * (st[top] - h[i]);
				tp += num[top],top--;
			}


			st[++top] = h[i],num[top] = tp;
			if (sa[i] > len1) ans += tot;
		}
	}

	
	return ans;
}


int main()
{
	int i,j,k,t,cas = 0;
	int vis[MAX],tp,tot;
	char str[MAX];
	
	
	scanf("%d",&t);
	while (t--) {
		
		scanf("%s",str);
		len1 = len = strlen(str);

		tot = (len + 1) * len / 2;
		memset(vis,0,sizeof(vis));
		for (i = 0; i < len; ++i)
			arr[i] = str[i],vis[str[i]]++;
		arr[i] = '$',i++;
		for (j = 0; j < len; ++j)
			arr[i+j] = str[j];
		arr[i+j] = 0,len = i + j;
		

		Da(arr,len+1,150);
		CalHeight(arr,len);
		
		
		ans1 = ans2 = 0;
		for (i = 'a'; i <= 'z'; ++i)
			if (vis[i] > ans1) ans1 = vis[i];


		ans2 = Solve(len);
		printf("Case %d: Alice:%.10lf Bob:%.10lf\n",++cas,ans1/tot,ans2/tot/tot);
	}
}

本文ZeroClock原创,但可以转载,因为我们是兄弟。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值