钉耙编程(4)

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(待补)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值