1009 昵称检索
Problem Description
解析:
对于数字(日期):f[i][j]:从字符串的 1 位置开始到 i 位置,数字 j 出现的最靠右的位置。
对于字母(名字):f[i][j]:从字符串的末尾位置到i位置,字母j出现的最靠左的位置。
对1年365天的日期进行枚举;判断串中是否有该日期,并且代码中判断出来的是该日期出现的最靠后的一个的第一个数字的下标。
并用一个数组记录下来,suf[i]:i位置是suf[i]个日期的开始位置。
对suf求后缀和:suf[i]:i位置后存在suf[i]个日期。
对name进行枚举;判断串中是否有该name,并且代码中判断出来的是该name出现的最靠前的一个的最后一个字母的下标。
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=1000005;
int day[13]={0,31,29,31,30,31,30,31,31,30,31,30,31};
int n,m;
int vis[26],f[N][26],suf[N];
int godate(int*s){
int x=m+1;
for(int i=3;~i&&x;i--){
x--; x=f[x][s[i]];
}
return x;
}
int goname(char*s){
int x=0;
for(int i=0;i<strlen(s)&&x<m+1;i++){
x++; x=f[x][s[i]-'a'];
}
return x;
}
int main(){
freopen("D:\\1009(4).txt","w",stdout);
int t; cin>>t;
while(t--){
int ans=0;
scanf("%d%d",&n,&m);
char s[N]; scanf("%s",s+1);
memset(vis,0,sizeof(vis));
memset(suf,0,sizeof(suf));
//从1到i 数字j出现的最靠右的位置
for(int i=1;i<=m;i++){
if(s[i]>='0'&&s[i]<='9') vis[s[i]-'0']=i;
for(int j=0;j<=9;j++)
f[i][j]=vis[j];
}
for(int i=1;i<=12;i++)for(int j=0;j<=day[i];j++){
int pool[4];
pool[0]=i/10;
pool[1]=i%10;
pool[2]=j/10;
pool[3]=j%10;
int x=godate(pool);
if(x) suf[x]++;
}
for(int i=m;i>1;i--) suf[i-1]+=suf[i];
//从m到i 字母j出现的最靠左的位置
for(int j=0;j<26;j++) vis[j]=m+1;
for(int i=m;i;i--){
if(s[i]>='a'&&s[i]<='z'){
vis[s[i]-'a']=i;
for(int j=0;j<26;j++) f[i][j]=vis[j];
}
}
while(n--){
char a[25];
scanf("%s",a+1);
int x=goname(a+1);
if(x<m) ans+=suf[x+1];
}
printf("%d\n",ans);
}
return 0;
}
1003(待补)