题目
小明正在分析一本小说中的人物相关性。他想知道在小说中 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 个字符。
注意:
- Alice 和 Bob 是大小写敏感的,alice 或 bob 等并不计算在内。
- 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)]
)。
易错点
- 下标和间隔之间的关系:间隔是指一个名字的末尾到另一个名字开头之间包含的char的数目;另注意名字本身的长度;
- 输出可能很大,需要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;
}