我就先不多啰嗦了,题目内容如下。
题目描述
苦逼运维修好学校的土豆服务器,复读终止;
但一位恶人及时地将服务器下锅,开始复读。
现在服务器输出了n (1<=n<=200000) 个数,需要求出其中出现次数最多的那个数 x。
其中x出现的次数 c > n / 2, 非常符合复读机本质。
由于服务器已经下锅,运行混乱,对于任意输出的数u, 0<=u<=10^100。
输入描述
第1行一个整数n。
第2行n个整数,代表输出的整数。
本题只有一组测试数据。
输出描述
一个整数x。
样例输入
10
6 6 6 7 7 7 7 7 7 7
样例输出
7
不难看出,这是典型的主元素问题,其中主元素的个数严格大于 n / 2 。
这个题目的特殊之处就是数据的范围是0<=u<=10^100,对于这种大数问题,用int类型或者long long int类型是远远不够的,int类型的范围也就是21亿这样子,long long int类型也多不到哪去,远远小于给定范围。所以这个范围对使用C/C++的用户来说,就几乎等于告诉你这题的数据要用字符串来处理。
首先,我们来说一下数据在int范围内的解法。容易想到,先将给定数据排序,由于主元素个数严格大于 n / 2 ,就不难看出不论元素总的个数是奇数还是偶数,处于 n / 2 这个位置的元素必定是主元素。这样来看,最终的时间复杂度就由排序的时间复杂度来决定,快速排序复杂度是O(nlogn),用这种排序法便可以较快得出结果。
然后,我们只要对快速排序的过程稍作修改,将数据的比较改成字符串的比较,借助strcmp()这个函数,再者就是在字符串交换时借助strcpy()函数,当然使用这两个函数之前要包含字符串头文件。这样做完之后,我兴奋地提交了一次,然而结果是超时了QAQ。
我想,既然快速排序法超时了,这个题目必定就有更巧妙地算法。我考虑了一会,便想到了一个非常节省时间的方法,将时间复杂度降到了O(n), 提交的结果也很是让人激动,Accepted。
话不多说,先贴上代码为敬。
#include <stdio.h>
#include <string.h>
char a[210000][110];
int main()
{
char temp[110];
int n,i,head;
scanf("%d",&n);
head=1;
for (int i=1; i<=n;i++)
{
scanf("%s",temp);
if(head==1)
strcpy(a[head++],temp);
else if(strcmp(temp,a[head-1])==0)
strcpy(a[head++],temp);
else
head--;
}
printf("%s\n",a[1]);
return 0;
}
这个方法就是采用删除法(或者说是抵消法),只要输入的下一个数和前一个数不同,就把这两个数都删除掉(或者说这两个数互相抵消),然后由于主元素的个数严格大于 n / 2 ,不难想到最终剩下的元素都是主元素,所以只要输出第一个元素就是最终结果。
好了,饺子对这个题目的理解目前就这么多,希望大佬们给出更妙的解法。