题目
题目大意:
给出两个字符串s和t,现有一个空字符串z
每次可以取s的任意一个子序列加到z后面
问:至少要取多少次才能让z等价于t,若无法组成输出-1
思路:
方法一:一个类似于建树的思想
序列自动机用nxt数组存入字符信息
nxt[i][j]:字符串第i位后的字母(‘a’+j)最早出现的位置
当前字符更新到当前字符在原串中从后向前最晚出现的位置(init())
查询此次在s中取的子序列到t的位置(find)
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
int nxt[N][30];string s,st;
void init(int n){
for(int i=n;i>=1;i--){//从后往前遍历
for(int j=0;j<26;j++)nxt[i-1][j]=nxt[i][j];
nxt[i-1][s[i-1]-'a']=i;
}
}
int find(int pos){
if(nxt[0][st[pos]-'a']==0)return -1;
for(int i=pos,cmp=0;i<st.size();i++){
cmp=nxt[cmp][st[i]-'a'];
if(cmp==0)return i;
}
return -625;
}
signed main(){
int t,ans;cin>>t;
while(t--){
ans=0;
memset(nxt,0,sizeof(nxt));
int ans=0;
cin>>s>>st;
init(s.size());
int pos=0;
while(1){
ans++;
pos=find(pos);
if(pos==-1||pos==-625)break;
}
if(pos==-1)cout<<-1<<endl;
else cout<<ans<<endl;
}
return 0;
}
方法二:
用数组v存入字符串s的信息
v[i][j]:字符’a’+i在s中的第j个在s中的位置
二分查找v[x]中大于pos且离pos最近的x所在的位置
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
vector<int>v[30];
int siz[30];
signed main(){
int t;cin>>t;string s,st;
while(t--){
cin>>s>>st;
memset(siz,0,sizeof(siz));
int lens=s.size(),lent=st.size();
for(int i=0;i<26;i++)v[i].clear();
for(int i=0;i<lens;i++){
v[s[i]-'a'].emplace_back(i);
siz[s[i]-'a']++;
}
int flag=0,pos=-1,ans=1;
for(int i=0,x,b;i<lent;i++){
x=st[i]-'a';
if(siz[x]==0){
flag=1;break;
}
if(pos>=v[x][siz[x]-1]){
ans++;
pos=v[x][0];
}else{
b=upper_bound(v[x].begin(),v[x].end(),pos)-v[x].begin();
pos=v[x][b];
}
}
if(flag){
cout<<-1<<endl;
}else{
cout<<ans<<endl;
}
}
return 0;
}