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原创,但可以转载,因为我们是兄弟。

你可能感兴趣的:(JOIN,Date,String,Integer,each,output)