2014百度之星复赛解题报告:The Patterns

The Patterns
时间限制: 5s  内存限制: 65536K

问题描述:
对于一个1到n的全排列Q,我们定义Q对应的字符串SQ,其中

C497-4004-1.jpg
例如对于n=5,Q=32154, SQ=DDUD。
本任务为给定一个仅包含字符’U’与’D’长度为m的模式串P以及一个正整数n,求解1到n的任意全排列中模式串出现次数的期望值。例如某个SQ=UUU,P=UU,则P出现次数为2次。
例如n=3,P=U,则1到n的所有全排列方案对应的模式串P出现次数分别为
  
Q
  
                           SQ
P出现次数
123
UU
2
132
UD
1
213
DU
1
231
UD
1
312
DU
1
321
DD
0
则1到n的任意全排列中模式串出现次数的期望值
c9c99608-b685-475b-8d7f-32d67dbf5182.JPG
为了避免浮点数精度误差,请输出 ac3665fd-20ea-4b6d-8a63-5860c86936d3.JPG ,其中mod为取模操作,具体定义以及运算规则参见: 链接地址

输入
输入数据第一行为一个整数T1 <= T <= 3333),表示有 T 组测试数据。
下面T组数据,每组数据第一行包含2个正整数n, m,接下来一行包含一个字符串P(仅包含字符’U’与’D’)
数据规模:1 <= m <= 1000       m<n<=1000000

输出
对于第k组数据,第一行输出Case #k:,第二行输出  ac3665fd-20ea-4b6d-8a63-5860c86936d3.JPG 

样例输入
5
4 3
UUU
4 2
UU
10 8
UUUUDDDD
2 1
U
2 1
D

样例输出
Case #1:
1
Case #2:
8
Case #3:
1400
Case #4:
1
Case #5:
1


解题报告:

首先计算n=m+1的情况下,1到n的所有全排列中能匹配P的次数。令其为A,则此时期望为
ac93e9fe-e553-4bfe-be69-73a04fa66f83.JPG
令事件X_i表示在某个排列Q的第i个元素开始,之后的m+1个元素符合模式串P。则
dcebc18c-c164-4ecc-b4e2-b13db57de15d.JPG
根据期望的线性
304d7fcc-97dc-49e4-8c39-f7e346992b3e.JPG
d806382d-8cdf-4acf-96de-d4d54282da38.JPG
因此只需要求解得到A即可得到答案。
计算A采用动态规划算法
f[i,j] 表示前 i 个数字,最后一个数字所在前i个数字中的排名为j (排名从1开始)
则有
f[1,1] = 1
for i = 2..m + 1
  for j = 1..i                        
                   if p[i-2] == 'U'
                                     f[j]= f[i-1][j-1]+f[i-1][j-2]+…+f[i-1][1];
                   else
                                     f[j]= f[i-1][j]+f[i-1][j+1]…f[i-1][i-1];
时间复杂度为O(m^3)
而通过部分和优化可以优化到O(m^2)
解题代码:
#include <functional>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <numeric>
#include <cstring>
#include <climits>
#include <cassert>
#include <cstdio>
#include <string>
#include <vector>
#include <bitset>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <list>
#include <set>
#include <map>

using namespace std;
typedef long long LL;

const int P = (int)1e9 + 7 ;

const int MAXN = 1234;

int count( char a[], char b[]) {
        int ret = 0;
        int rem = strlen(a);
        int m = strlen(b);
        
        for(int i = 0; a; ++ i, -- rem) if(rem >= m){
                bool ok = 1;
                for(int j = 0; b[j]; ++ j) {
                        if(a[i+j] != b[j]) {
                                ok = 0;
                                break;
                        }
                }
                if(ok) ret ++ ;
        }
        return ret;
}

int a[ MAXN ];
LL f[ MAXN ][ MAXN ] , g[ MAXN ][ MAXN ];

void inc( LL &a, LL b) {
        b%=P;
        if(b<0) b+=P;
        a += b;
        if(a>=P) a-= P;
}

LL dp( int m, char p[]){
        
        f[1][1] = 1 ;
        g[1][0] = 0;g[1][1] = 1;
        
        for(int i = 2; i <= m + 1; ++ i) {
                g[0]=0;
                for(int j = 1; j <= i; ++ j) {
                        if( p[i-2] == 'U') {
                                f[j] = g[i-1][j-1];
                        }else {
                                f[j] = ((g[i-1][i-1] - g[i-1][j-1])%P+P)%P;
                        }
                        g[j] = (g[j-1] + f[j])%P;
                }
        }
        LL ans = 0;
        for(int i = 1; i <= m + 1; ++ i) inc( ans , f[m+1]);
        return ans;
}

LL bf(int n, int m, char p[]){
        char s[MAXN];
        s[n-1]=0;
        LL ans = 0;
        
        for(int i = 0; i < n; ++ i) a=i;
        do{
                for(int i = 0; i < n-1 ; ++ i)
                        s = (a<a[i+1]?'U':'D');
                ans += count( s , p ) ;
        }while(std::next_permutation(a,a+n));
        
        return ans;
}

LL bf_solve( int n, int m, char p[]) {
        LL _one = bf(m+1,m,p) * (n-m);
        for(int i = m+2; i <= n; ++ i) _one *= i ;
        return _one;
}

LL my_solve( int n, int m, char p[]) {
        LL _one = dp(m,p) * ((LL)n-m)%P;
        for(int i = m+2; i <= n; ++ i) _one =_one* i%P ;
        return _one;
}

void bf_check(){
        int n, m;
        char p[MAXN];
        for(n = 2; n <= 9; ++ n) {
                for(m = 1; m < n; ++ m) {
                        p[m]=0;
                        for(int s = 0; s < (1<<m); ++ s) {
                                for(int i = 0; i < m; ++ i) p=(s&(1<<i))?'U':'D';
                                cout << n <<" " << m <<" " << p << endl;
                                LL _a = bf( n, m, p ) ;
                                LL _b = my_solve(n,m,p);
                                cout << _a << endl;
                                if(_a == _b) {
                                        static int cas = 0; ++ cas;
                                        cout <<"correct @" << cas << endl;
                                }else {
                                        cout << "error!" << endl;
                                        return;
                                }
                        }
                }
        }
}

char p[ MAXN ];
int n, m ;

int main() {
        //bf_check();
        int T;
        int K = 1;
    cin >> T;
        while(T -- ) {
                scanf("%d%d%s", &n, &m, p);
                printf ("Case #%d:\n%d\n" , K++, (int) my_solve(n,m,p) );
        }
        return 0;
}


转载于:https://www.cnblogs.com/hosealeo/p/4190494.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值