题目描述:
两个等长的由大写英文字母构成的字符串a和b,从a中选择连续子串x,从b中选出连续子串y。子串x与子串y的长度相等。
定义函数f(x,y)为满足条件xi=yi(1<=i<=|x|)的i的个数,计算f(x,y)的数学期望。
输入:
第一行输入n(1<=n<=2*10^5),表示a和b的长度
第二行输入字符串a
第三行输入字符串b
输出:
输出一个实数表示f(x,y)的期望,答案保留6位小数。
样例:
题目为什么要给图片样例呢??!
算法标签:期望dp
思路:
详细的在sgx神犇里有图文并茂的介绍: https://blog.csdn.net/qq_41717018/article/details/83217701
这题总体还是很妙的,运用整体思想,我们考虑单个位置上有匹配会造成多少贡献,以下:
当i<j时,我们发现,这个字母O,由于在B的开头一定在[j-i+1,j]中,可以找到会带来1的贡献的子串,由于B的结尾一定在[j,n]中,所以在A若在此位有贡献,则A的结尾一定在[i,i+n-j]中,所以总贡献即i*(n-j+1)。
当i>j时,则类似i<j的分析,则贡献就是(n-i+1)*j。
于是在一个子串中统计前缀出现的字母i出现的位置之和,和后缀字幕出现n-pos+1之和。
以下代码:
#include<bits/stdc++.h> #define il inline #define D double #define LL long long #define _(d) while(d(isdigit(ch=getchar()))) using namespace std; const int N=2e5+5;char a[N],b[N];int n;LL f[30][N],g[30][N],sum; il int read(){int x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch^48;_()x=(x<<1)+(x<<3)+(ch^48);return f*x;} int main() { n=read();scanf(" %s",a+1);scanf(" %s",b+1); for(int i=1;i<=n;i++){ for(int j=1;j<=26;j++)f[j][i]=f[j][i-1];f[a[i]-'A'+1][i]+=(LL)i; } for(int i=n;i;i--){ for(int j=1;j<=26;j++)g[j][i]=g[j][i+1];g[a[i]-'A'+1][i]+=1ll*(n-i+1); } for(int i=1;i<=n;i++) sum+=(LL)i*g[b[i]-'A'+1][i+1]+1ll*(n-i+1)*f[b[i]-'A'+1][i]; printf("%.6lf ",(D)sum*6.0/(1.0*(1.0*(D)(n+1)*(D)(2*n+1)*(D)n))); return 0; }