原题链接
思路:
贪心 + 二分 + 前缀和
贪心指对于字符串 b 中的每个字母,选择串 a 中” 可选区域” 中最前面那个.
“可选区域” 由前一个字母决定。若可选区域中没有则答案 + 1 并在整个串 a 中寻找,
若串 a 也没有则无解.
其中,“选择串 a 中’可选区域’中最前面那个” 由二分 + 前缀和完成.
对于每个字母统计区间 [1, n] 中该字母出现次数,
二分时便可通过前缀和判断某区间是否有所要寻找的字母.
时间复杂度:预处理 O (n)(可能常数较大), 计算 O (mlogn),
其中 n,m 分别为串 a 与串 b 的长度.
代码:
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
| #include <cstdio> #include <cstring> #include <string> #include <iostream> using namespace std; const int Mn(100500); int ise[Mn][30]; int main() { int T; scanf("%d",&T); while(T--) { string a,b; cin >> a >> b; memset(ise[0],0,sizeof ise[0]); for(int i(1);i<=a.size();++i) { memcpy(ise[i],ise[i-1],sizeof ise[i]); ++ise[i][a[i-1]-'a']; } memcpy(ise[a.size()+1],ise[a.size()],sizeof ise[0]); for(int i(0);i<30;++i) ++ise[a.size()+1][i]; int p(0); int ans(1); for(int i(0);i<b.size();++i) { if(ise[a.size()][b[i]-'a']==0) { ans = -1; break; } else if(ise[a.size()][b[i]-'a']==ise[p][b[i]-'a']) { ++ans; p=0; } int l(p+1),r(a.size()+1); while(r-l>3) { int m((l+r)>>1); if(ise[m][b[i]-'a']!=ise[l-1][b[i]-'a']) r = m+1; else l = m+1; } for(int j(l);j<r;++j) if(ise[j][b[i]-'a']!=ise[j-1][b[i]-'a']) { p = j; break; } } printf("%d\n",ans); } return 0; }
|