题目 2309: 蓝桥杯2019年第十届省赛真题-人物相关性分析

题目

小明正在分析一本小说中的人物相关性。他想知道在小说中 Alice 和 Bob 有多少次同时出现。

更准确的说,小明定义 Alice 和 Bob“同时出现”的意思是:在小说文本 中 Alice 和 Bob 之间不超过 K 个字符。

例如以下文本:
This is a story about Alice and Bob. Alice wants to send a private message to Bob. 假设 K = 20,则 Alice 和 Bob 同时出现了 2 次,分别是”Alice and Bob”和”Bob. Alice”。前者 Alice 和 Bob 之间有 5 个字符,后者有 2 个字符。

注意:

  1. Alice 和 Bob 是大小写敏感的,alice 或 bob 等并不计算在内。
  2. Alice 和 Bob 应为单独的单词,前后可以有标点符号和空格,但是不能

有字母。例如 Bobbi 並不算出现了 Bob。

输入
第一行包含一个整数 K。 第二行包含一行字符串,只包含大小写字母、标点符号和空格。长度不超过 1000000。(对于所有评测用例,1≤ K ≤1000000。)

输出
输出一个整数,表示 Alice 和 Bob 同时出现的次数

样例输入

20
This is a story about Alice and Bob. Alice wants to send a private message to Bob.

样例输出

2

解题思路

首先想到的思路是逐个字符读入,如果读到’A’或者’B’,即对单词进行判断,如果是,用hash散列记录下单词结尾的位置,以1表示“Alice”,2表示“Bob”,并用for循环向前查找K个,查看是否有另一个单词与之组成“Alice 和 Bob”。但超时(73分,代码可见文末“错误代码”部分)。

由此想到减少循环次数,显然,读入字符串的while和scanf相关的循环是不可减少的f,因此,想到删去“for向前查找K个是否存在另一个名字”的循环(错误代码中的for (i=k-6;i>max((k-7-K),0);i--)for (i=k-4;i>max(0,(k-5-K));i--)部分)。以while之外的累加数组代替之。

累加数组h_numA和h_numB记录的是,位置为i之前(包含i)共有多少个Alice/Bob。当读入hash散列h的第i个数时,如果该位置上不是任何名字的结尾,则有h_numA[i] = h_numA[i-1],h_numB[i] = h_numB[i-1];若该位置恰好是某个名字的末尾,则有h_numA[i] = h_numA[i-1]+1 或者 h_numB[i] = h_numB[i-1]+1,与此同时,可以利用该次循环,计算出Alice 和 Bob 同时出现的次数(比如:若读入的是Alice,那么Alice 和 Bob 同时出现的次数num有num = h_numB[i]-h_numB[max(0,i-K-5)])。

易错点

  1. 下标和间隔之间的关系:间隔是指一个名字的末尾到另一个名字开头之间包含的char的数目;另注意名字本身的长度;
  2. 输出可能很大,需要long int类型。

代码

#include<stdio.h>
int h[1000001];//hash,字符串长度不超过 1000000
int h_numA[1000001];//统计当前位置前K个位置,"Alice"的个数
int h_numB[1000001];//统计当前位置前K个位置,"Bob"的个数

int main()
{
    int K,i,t,k=0;
    long int num=0;
	char temp;
	char A[4] = {'l','i','c','e'};
	char B[2] = {'o','b'};
	int end[2] = {4,2};
	int mark[2] = {1,2};//在hash散列中记录是哪一种名字
	scanf("%d",&K);
	while ((temp=getchar())!=EOF){
	    k++;//读入的数据位数
	    if (temp=='A')//Alice
	        t = 0;
	    else if (temp=='B')//Bob
	        t = 1;
	    else
	        continue;

        for (i=0;i<end[t];i++)//检验后面是否是lice
        {
            if (scanf("%c",&temp)!=EOF)//有字符串可以读入
            {
                k++;
                if (t==0)//A开头
                {
                    if (temp!=A[i])
                        break;
                }
                else if (temp!=B[i])
                        break;
            }
            else //无字符可以读入
                break;
        }
        
        if (i!=end[t])//不存在Alice或者Bob
            continue;
        else//存在Alice或者Bob
        {
            if (scanf("%c",&temp)!=EOF)
            { 
                k++;
                if((temp>64 && temp<91) || (temp>96 && temp<123))//如果后面是字母
                    continue;
                h[k-1] = mark[t];//表示Alice结尾所在位置
            }
            else
            {
                h[k] = mark[t];
                break;//无字符可以读入
            }
        }
	}
	
	for (i=1;i<k;i++)//统计个数
	{
	    h_numA[i] = h_numA[i-1];
	    h_numB[i] = h_numB[i-1];//累加
	    if (h[i]==1)//包含当前位置上的
	    {
	        h_numA[i]++;
	        t = ((i-K-5)>0)?(i-K-5):0;
	        num+=(h_numB[i]-h_numB[t]);
	    }
	    else if (h[i]==2)
	    {
	        h_numB[i]++;
	        t = ((i-K-3)>0)?(i-K-3):0;
	        num+=(h_numA[i]-h_numA[t]);
	    }
	}
	printf("%ld",num);
	return 0;
}

错误代码

时间超限代码(73分):

#include<stdio.h>
int h[1000001];//hash,字符串长度不超过 1000000

int max(int a, int b){
    return (a>b)?a:b;
}

int main()
{
    int K,i,gap,k=0,num=0;
	char temp;
	char A[4] = {'l','i','c','e'};
	char B[2] = {'o','b'};
	scanf("%d",&K);
	while ((temp=getchar())!=EOF){
	    k++;//读入的数据位数
	    if (temp=='A')//Alice
	    {
	        for (i=0;i<4;i++)//检验后面是否是lice
	        {
	            if (scanf("%c",&temp)!=EOF)//有字符串可以读入
	            {
	                k++;
	                if (temp!=A[i])
	                    break;
	            }
	            else //无字符可以读入
	                break;
	        }
	        if (i!=4)//不存在Alice
	            continue;
	        else//存在Alice
	        {
	            if (scanf("%c",&temp)!=EOF)
	            { 
	                k++;
	                if((temp>64 && temp<91) || (temp>96 && temp<123))//如果后面是字母
	                    continue;
	            }
	            h[k-1] = 1;//表示Alice结尾所在位置
	            for (i=k-6;i>max((k-7-K),0);i--)
	                if (h[i]==2)
	                    num++;//遇到Bob这个单词了
	        }
	            
	    }
	    else if (temp=='B')//Bob
	    {
	        for (i=0;i<2;i++)//检验后面是否是ob
	        {
	            if (scanf("%c",&temp)!=EOF)//有字符串可以读入
	            {
	                k++;
	                if (temp!=B[i])
	                    break;
	            }
	            else //无字符可以读入
	                break;
	        }
	        if (i!=2)//不存在Alice
	            continue;
	        else//存在Alice
	        {
	            if (scanf("%c",&temp)!=EOF)
	            { 
	                k++;
	                if((temp>64 && temp<91) || (temp>96 && temp<123))//如果后面是字母
	                    continue;
	            }
	            h[k-1] = 2;//表示Bob结尾所在位置
	            for (i=k-4;i>max(0,(k-5-K));i--)
	                if (h[i]==1)
	                    num++;//遇到Alice这个单词了
	        }
	    }
	}
	printf("%d",num);
	return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值