noip2015 子串


P2679 子串

    • 369通過
    • 2K提交
  • 題目提供者2014白永忻
  • 標簽動態規划字符串NOIp提高組2015
  • 難度提高+/省選-

提交該題 討論 題解 記錄

最新討論

  • 暫時沒有討論

題目背景

題目描述

有兩個僅包含小寫英文字母的字符串 A 和 B。現在要從字符串 A 中取出 k 個互不重

疊的非空子串,然后把這 k 個子串按照其在字符串 A 中出現的順序依次連接起來得到一 個新的字符串,請問有多少種方案可以使得這個新串與字符串 B 相等?注意:子串取出 的位置不同也認為是不同的方案。

輸入輸出格式

輸入格式:

 

輸入文件名為 substring.in。

第一行是三個正整數 n,m,k,分別表示字符串 A 的長度,字符串 B 的長度,以及問

題描述中所提到的 k,每兩個整數之間用一個空格隔開。 第二行包含一個長度為 n 的字符串,表示字符串 A。 第三行包含一個長度為 m 的字符串,表示字符串 B。

 

輸出格式:

 

輸出文件名為 substring.out。 輸出共一行,包含一個整數,表示所求方案數。由於答案可能很大,所以這里要求[b]輸出答案對 1,000,000,007 取模的結果。[/b]

 

輸入輸出樣例

輸入樣例#1:
6 3 1 
aabaab 
aab
輸出樣例#1:
2
輸入樣例#2:
6 3 2 
aabaab 
aab
輸出樣例#2:
7
輸入樣例#3:
6 3 3 
aabaab 
aab
輸出樣例#3:
7

說明

對於第 1 組數據:1≤n≤500,1≤m≤50,k=1;

對於第 2 組至第 3 組數據:1≤n≤500,1≤m≤50,k=2; 對於第 4 組至第 5 組數據:1≤n≤500,1≤m≤50,k=m; 對於第 1 組至第 7 組數據:1≤n≤500,1≤m≤50,1≤k≤m; 對於第 1 組至第 9 組數據:1≤n≤1000,1≤m≤100,1≤k≤m; 對於所有 10 組數據:1≤n≤1000,1≤m≤200,1≤k≤m。

分析:比較怕數論,dp,高精度和字符串,來一個就覺得比較難,來兩個做不出來了......而網上的題解又比較少.

很明顯,這是一道dp題,然后來想想怎么表示狀態,對答案有影響的就是A串的第i個字符,B串的第j個字符,和k個子串,簡單來說就是和選取的字符和子串的數量有關.那么設f[i][j][kk]表示在A串的前i個字符中選kk個子串匹配B串的前j個字符的方案數.求方案數可以采用加法原理,考慮A串的第i個字符,那么這個字符的決策只有取或不取,很明顯,加法原理,把不取的方案數和取的方案數加起來就可以,但是狀態的定義並不能看出這個字符到底取不取,或者說並不能推出結果來,怎么辦呢?

那么就用一個數組s[i][j][kk]來表示在A串的前i個字符中選kk個子串匹配B串的前j個字符的方案數,A串的第i個字符會被取到.那么這個s數組該怎么推出來呢?可以發現,如果取第i個字符也有2種可能,因為kk是一定的,第i個字符可能和第i-1個字符合並成一個子串,那么從s[i-1][j][kk]轉移過來,也可能不和第i-1個字符合並成一個子串,那么就要新開一個子串,故kk一定從kk-1轉移過來,根據加法原理,那么s[i][j][kk] = s[i-1][j-1][kk] + f[i-1][j-1][kk-1].

還有一個問題,f數組該怎么推呢?根據之前分析的,把不取和取的加上來即可,即f[i][j][kk] = s[i][j][kk] + f[i-1][j][kk],其實可以發現,f數組就相當於前綴和數組.

還有一個問題:這是一個三維的狀態轉移方程!空間不一定開的下,再看數據范圍,這絕對MLE,怎么辦?注意到i只能從i或i-1轉移過來,可以想到滾動數組,滾動數組有一個比較好些的寫法就是把第一維全部加上&1,或者干脆弄兩個變量處理完一組數據就交換即可.

本蒟蒻水平很低,如果神犇發現了錯誤,望指出QAQ

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

int n, m, k;

long long f[2][205][205], s[2][205][205];
char a[1005], b[205];
const int mod = 1000000007;

int main()
{
    scanf("%d%d%d", &n, &m, &k);
    scanf("%s", a + 1);
    scanf("%s", b + 1);
    int now = 1, last = 0;
    f[0][0][0] = 1;
    for (int i = 1; i <= n; i++)
    {
        f[now][0][0] = 1;
        for (int j = 1; j <= m; j++)
            for (int kk = 1; kk <= k; kk++)
            {
                if (a[i] == b[j])
                    s[now][j][kk] = (s[last][j - 1][kk] + f[last][j - 1][kk - 1]) % mod;
                else
                    s[now][j][kk] = 0;
                f[now][j][kk] = (f[last][j][kk] + s[now][j][kk]) % mod;
            }
        swap(now, last); //一定要變,否則就是一直自己跟自己轉移.
    }
    printf("%lld\n", f[last][m][k]);

    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM