常用next[i][j]来表示从第i个位置开始,字符串j出现的第一个位置或其他含义
从而达到快速判断是否为模式串的(子序列)或者优化dp的目的
例题:
小Z的笔记
#include <bits/stdc++.h> using namespace std; int n,m; const int N = 1e5 + 10; char s[N]; char vis[26][26]; int nex[N][26]; int dp[N]; int main(){ scanf("%d",&n); scanf("%s",s + 1); scanf("%d",&m); for(int i=1;i<=m;i++){ char a,b; cin >> a >> b; vis[a-'a'][b-'a'] = vis[b-'a'][a-'a'] = 1; } for(int i=1;i<=n;i++){ for(int j=0;j<26;j++) nex[i][j] = nex[i-1][j]; nex[i][s[i] - 'a'] = i; } for(int i=1;i<=n;i++){ int p = s[i] - 'a'; dp[i] = i; for(int j=0;j<26;j++){ if(!vis[p][j]){ int pos = nex[i-1][j]; dp[i] = min(dp[i],i-pos-1+dp[pos]); } } } cout << dp[n] << endl; return 0; }
#include<bits/stdc++.h> using namespace std; const int N = 1e5+100; const int INF = 0x3f3f3f3f; char s[N]; char t[N]; struct sub_AM{ int nxt[N][27]; void init(char *s){ int l=strlen(s); for(int i=0;i<26;i++) nxt[l][i]=INF; for(int i=l-1;i>=0;i--){ for(int j=0;j<26;j++){ nxt[i][j]=nxt[i+1][j]; } nxt[i][s[i]-'a']=i; } } bool find(char *t){ int pos=-1; int l=strlen(t); for(int i=0;i<l;i++){ pos=nxt[pos+1][t[i]-'a']; if(pos==INF) return 0; } return 1; } }solve; int main(){ scanf("%s",s); solve.init(s); int q; scanf("%d",&q); while(q--){ scanf("%s",t); if(solve.find(t)){ puts("YES"); } else puts("NO"); } return 0; }
当然正着和反着皆可
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10; char s[N]; char t[N]; int nex[N][26]; int n,len; void solve(){ for(int i=n+1,j=len;j>=1;j--){ i = nex[i-1][t[j] - 'a']; if(i == 0){ puts("NO"); return; } } puts("YES"); } int main(){ scanf("%s",s + 1); n = strlen(s + 1); //printf("%s\n",s + 1); for(int i=1;i<=n;i++){ for(int j=0;j<26;j++) nex[i][j] = nex[i-1][j]; nex[i][s[i] - 'a'] = i; } int m; cin >> m; while(m--){ scanf("%s",t + 1); len = strlen(t + 1); //printf("%s\n",t + 1); solve(); } return 0; }
以下参考了青烟大佬的题解
hdu6586
题意:需要找到一个字典序最小的长度为k的满足
且a~z的第i个字符出现次数必须在[,
]之间
思路:
预处理记录下每个位置后面每个字符的数量,和每个字符的位置,这里我们就通过位置关系,枚举字符来判断要这个字符是否符合条件,l和r记录该字符还需要多少个,记录两个值
#pragma GCC optimize("-Ofast","-funroll-all-loops") #include<bits/stdc++.h> //#define int long long using namespace std; const int N=1e5+10; int nex[N][26],sum[N][26],n,k,ok,pos,cnt[26],cl[26],cr[26]; char str[N],res[N]; inline int check(int p,int ch){ int now=nex[pos][ch],nd=k-p-1,num=0,num1=0; if(now>n||cnt[ch]>=cr[ch]) return 0; for(int i=0;i<26;i++){ if(cnt[i]+sum[now+1][i]+(i==ch)<cl[i]) return 0; if(cl[i]>cnt[i]+(ch==i)) num1+=cl[i]-cnt[i]-(ch==i); num+=cr[i]-cnt[i]-(i==ch); } return num>=nd&&num1<=nd; } inline void solve(){ n=strlen(str+1); ok=1; pos=1; res[k+1]='\0'; for(int i=0;i<26;i++) scanf("%d %d",&cl[i],&cr[i]); for(int i=0;i<26;i++) sum[n+1][i]=0,nex[n+1][i]=n+1,cnt[i]=0; for(int i=n;i>=1;i--){ for(int j=0;j<26;j++) sum[i][j]=sum[i+1][j],nex[i][j]=nex[i+1][j]; sum[i][str[i]-'a']++,nex[i][str[i]-'a']=i; } for(int i=1;i<=k&&ok;i++){ int flag=0; for(int j=0;j<26;j++) if(check(i-1,j)){ res[i]=j+'a'; flag=1; cnt[j]++; pos=nex[pos][j]+1; break; } if(!flag) ok=0; } if(!ok) puts("-1"); else printf("%s\n",res+1); } signed main(){ while(~scanf("%s %d",str+1,&k)) solve(); return 0; }