题目大意
题目描述
给定 n n n 个秘密单词 组成的列表,以及一个目标文本 T T T 。
目标文本 T T T 由小写英文字母和符号 ? 组成。其中 ? 是通配符,可以匹配秘密单词中的任意一个字母。
你需要求出有多少种方法,可以将整个文本 T T T 完全拆分为列表中的单词。
每个单词可以使用任意多次。
输出方案数对 1 0 9 + 7 10^9 + 7 1 0 9 + 7 取模的结果。
输入格式
第一行包含一个整数 n n n (1 ≤ n ≤ 100 1 \le n \le 100 1 ≤ n ≤ 1 0 0 )。
接下来 n n n 行,每行一个字符串 W i W_i W i (1 ≤ ∣ W i ∣ ≤ 10 1 \le |W_i| \le 10 1 ≤ ∣ W i ∣ ≤ 1 0 ),保证所有单词互不相同。
最后一行包含一个字符串 T T T (1 ≤ ∣ T ∣ ≤ 100 1 \le |T| \le 100 1 ≤ ∣ T ∣ ≤ 1 0 0 )。
样例 1
输出 1
样例 1 解释
目标文本为 ab?d。? 可以匹配任意字符,即此处的 ? 需要根据选用的单词被“填充”。
可能的拆分方式包括:
[a, b, a, d] (第三个单词为 a,此时 ? 匹配 a)
[a, b, b, d] (? 匹配 b)
[a, b, c, d] (? 匹配 c)
[a, b, d, d] (? 匹配 d)
[ab, a, d]
[ab, b, d]
[ab, c, d]
[ab, d, d]
[a, bc, d]
[a, b, cd]
[ab, cd]
样例 2
输出 2
样例 2 解释
目标文本为 ??。
可能的拆分方式包括:
[a, a]
[a, b]
[b, a]
[b, b]
思路讲解
采用线性 DP 的方式啊,采用线性 DP 的方式。
这题的陷阱就是啊,至少是我踩中的陷阱啊。就是采用了区间 DP 的方式,因为毕竟这个数据范围比较小嘛,很容易想到区间 DP 啊。不过区间 DP 它会,虽然说二叉分裂是不同的,但是划分方式是相同的,是会有这种情况的发生。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 for (int i=0 ;i<N;++i) { for (int len=1 ;len<=10 ;++len) { if (i-len+1 <0 ) { break ; } string_view s_view (s.data()+i-len+1 ,len) ; ll valid_num=0 ; for (auto &ls:len_words_mtx[len]) { bool ok=true ; for (int j=0 ;j<SZ (ls);++j) { if (ls[j]==s_view[j] || s_view[j]=='?' ) { }else { ok=false ; } } if (ok) { valid_num++; } } dp[i]+=(i-len<0 ?1 :dp[i-len])*valid_num; dp[i]%=mod; } }
AC代码
AC
https://codeforces.com/gym/106169/submission/363004216
源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 #include <bits/stdc++.h> #define all(vec) vec.begin(),vec.end() #define lson(o) (o<<1) #define rson(o) (o<<1|1) #define SZ(a) ((long long) a.size()) #define debug(var) cerr << #var <<" = [" <<var<<"]" <<"\n" ; #define debug1d(a) \ cerr << #a << " = [" ; \ for (int i = 0; i < (int)(a).size(); i++) \ cerr << (i ? ", " : "" ) << a[i]; \ cerr << "]\n" ; #define debug2d(a) \ cerr << #a << " = [\n" ; \ for (int i = 0; i < (int)(a).size(); i++) \ { \ cerr << " [" ; \ for (int j = 0; j < (int)(a[i]).size(); j++) \ cerr << (j ? ", " : "" ) << a[i][j]; \ cerr << "]\n" ; \ } \ cerr << "]\n" ; #define debug3d(a) \ cerr << #a << " = [\n" ; \ for (int i = 0; i < (int)(a).size(); i++) \ { \ cerr << " [\n" ; \ for (int j = 0; j < (int)(a[i]).size(); j++) \ { \ cerr << " [" ; \ for (int k = 0; k < (int)(a[i][j]).size(); k++) \ cerr << (k ? ", " : "" ) << a[i][j][k]; \ cerr << "]\n" ; \ } \ cerr << " ]\n" ; \ } \ cerr << "]\n" ; #define cend cerr<<"\n-----------\n" #define fsp(x) fixed<<setprecision(x) using namespace std;using ll = long long ;using ull = unsigned long long ;using DB = double ;using CD = complex<double >;static constexpr ll MAXN = (ll)1e6 +10 , INF = (1ll <<61 )-1 ;static constexpr ll mod = (ll)1e9 +7 ;static constexpr double eps = 1e-8 ;const double pi = acos (-1.0 );ll lT,testcase; void Solve () { ll num_words; cin >> num_words; vector<string> secret_word (num_words+2 ) ; vector<vector<string>> len_words_mtx (15 ); for (int i=1 ;i<=num_words;++i) { cin>>secret_word[i]; len_words_mtx[SZ (secret_word[i])].push_back (secret_word[i]); } string s; cin>>s; ll N=SZ (s); vector<ll> dp (N+2 ) ; for (int i=0 ;i<N;++i) { for (int len=1 ;len<=10 ;++len) { if (i-len+1 <0 ) { break ; } string_view s_view (s.data()+i-len+1 ,len) ; ll valid_num=0 ; for (auto &ls:len_words_mtx[len]) { bool ok=true ; for (int j=0 ;j<SZ (ls);++j) { if (ls[j]==s_view[j] || s_view[j]=='?' ) { }else { ok=false ; } } if (ok) { valid_num++; } } dp[i]+=(i-len<0 ?1 :dp[i-len])*valid_num; dp[i]%=mod; } } cout<<dp[N-1 ]<<"\n" ; } signed main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); cout.tie (nullptr ); #ifdef LOCAL cout.setf (ios::unitbuf); #endif Solve (); return 0 ; }
心路历程(WA,TLE,MLE……)
会算重复的区间 dp 做法
为什么区间 DP 会算重复呢?是这样子的,虽然我们采用了不同的二叉划分,虽然我们采用了不同的二叉划分,但是在采用不同二叉划分的段里面 呢,实际上有划分是完全相同的 。的东西的。
因此我们必须得要采用线性 DP 做法 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 auto dfs=[&](auto && self,ll l,ll r) -> void { if (vis[l][r]) { return ; } vis[l][r]=1 ; ll len=r-l+1 ; if (!len_words_mtx[len].empty ()) { for (auto &ls:len_words_mtx[len]) { bool ok=true ; for (int i=0 ;i<ls.size ();++i) { if (s[l+i]==ls[i] || s[l+i]=='?' ) { }else { ok=false ; break ; } } if (ok) { dp[l][r]++; } } } for (ll sp=l;sp<r;++sp) { self (self,l,sp); self (self,sp+1 ,r); dp[l][r]+=dp[l][sp]*dp[sp+1 ][r]; dp[l][r]%=mod; } };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 #include <bits/stdc++.h> #define all(vec) vec.begin(),vec.end() #define lson(o) (o<<1) #define rson(o) (o<<1|1) #define SZ(a) ((long long) a.size()) #define debug(var) cerr << #var <<" = [" <<var<<"]" <<"\n" ; #define debug1d(a) \ cerr << #a << " = [" ; \ for (int i = 0; i < (int)(a).size(); i++) \ cerr << (i ? ", " : "" ) << a[i]; \ cerr << "]\n" ; #define debug2d(a) \ cerr << #a << " = [\n" ; \ for (int i = 0; i < (int)(a).size(); i++) \ { \ cerr << " [" ; \ for (int j = 0; j < (int)(a[i]).size(); j++) \ cerr << (j ? ", " : "" ) << a[i][j]; \ cerr << "]\n" ; \ } \ cerr << "]\n" ; #define debug3d(a) \ cerr << #a << " = [\n" ; \ for (int i = 0; i < (int)(a).size(); i++) \ { \ cerr << " [\n" ; \ for (int j = 0; j < (int)(a[i]).size(); j++) \ { \ cerr << " [" ; \ for (int k = 0; k < (int)(a[i][j]).size(); k++) \ cerr << (k ? ", " : "" ) << a[i][j][k]; \ cerr << "]\n" ; \ } \ cerr << " ]\n" ; \ } \ cerr << "]\n" ; #define cend cerr<<"\n-----------\n" #define fsp(x) fixed<<setprecision(x) using namespace std;using ll = long long ;using ull = unsigned long long ;using DB = double ;using CD = complex<double >;static constexpr ll MAXN = (ll)1e6 +10 , INF = (1ll <<61 )-1 ;static constexpr ll mod = (ll)1e9 +7 ;static constexpr double eps = 1e-8 ;const double pi = acos (-1.0 );ll lT,testcase; void Solve () { ll N; cin >> N; vector<string> secret_word (N+2 ) ; vector<vector<string>> len_words_mtx (15 ); for (int i=1 ;i<=N;++i) { cin>>secret_word[i]; len_words_mtx[SZ (secret_word[i])].push_back (secret_word[i]); } string s; cin>>s; vector<vector<ll>> dp (SZ (s)+2 ,vector <ll>(SZ (s)+2 )); vector<vector<char >> vis (SZ (s)+2 ,vector <char >(SZ (s)+2 )); auto dfs=[&](auto && self,ll l,ll r) -> void { if (vis[l][r]) { return ; } vis[l][r]=1 ; ll len=r-l+1 ; if (!len_words_mtx[len].empty ()) { for (auto &ls:len_words_mtx[len]) { bool ok=true ; for (int i=0 ;i<ls.size ();++i) { if (s[l+i]==ls[i] || s[l+i]=='?' ) { }else { ok=false ; break ; } } if (ok) { dp[l][r]++; } } } for (ll sp=l;sp<r;++sp) { self (self,l,sp); self (self,sp+1 ,r); dp[l][r]+=dp[l][sp]*dp[sp+1 ][r]; dp[l][r]%=mod; } }; dfs (dfs,0 ,SZ (s)-1 ); cout<<dp[0 ][SZ (s)-1 ]<<"\n" ; } signed main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); cout.tie (nullptr ); #ifdef LOCAL cout.setf (ios::unitbuf); #endif Solve (); return 0 ; }