3785 What day is that day? 浅谈KMP在ACM竞赛中的暴力打表找规律中的应用

70 篇文章 0 订阅

原作链接  :http://www.cnblogs.com/kevince/p/3887827.html

首先声明一下,这里的规律指的是循环,即找到最小循环周期。

这么一说大家心里肯定有数了吧,“不就是next数组性质的应用嘛”,没错,正是如此。

在ACM的比赛中有些时候会遇到一些题目,可以或必须通过找出数据的规律来编写代码,这里我们专门来讨论下 如何运用KMP中next数组的性质 来寻找一个长数组中的最小循环周期。

先来看一道题

ZOJ 3785

What day is that day?

Time Limit: 2 Seconds      Memory Limit: 65536 KB

It's Saturday today, what day is it after 11 + 22 + 33 + ... + NN days?

Input

There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:

There is only one line containing one integer N (1 <= N <= 1000000000).

Output

For each test case, output one string indicating the day of week.

Sample Input
2
1
2
Sample Output
Sunday
Thursday
Hint

A week consists of Sunday, Monday, Tuesday, Wednesday, Thursday, Friday and Saturday.

 

题目的大意是知道今天是周六,让你求 f = 11 + 22 + 33 + ... + NN 这么多天之后是星期几。

也就是求f % 7对于每个输入的N的值。这题在网上一搜题解,都说是打表找规律,当然这题有两种找法,一是对于每个ii  % 7 的值都找规律。

这里我们打表可知 前100个值如下所示

1 4 6 4 3 1 0 1 1 4 2 1 6 0 1 2 5 1 5 1 0 1 4 1 4 4 6 0 1 1 3 2 6 1 0 1 2 2 1 2 6 0 1 4 6 4 3 1 0 1 1 4 2 1 6 0 1 2 5 1 5 1 0 1 4 1 4 4 6 0 1 1 3 2 6 1 0 1 2 2 1 2 6 0 1 4 6 4 3 1 0 1 1 4 2 1 6 0 1 2

有一种找规律的方法是当有数字等于第一个数的时候做个标记,再人工判断是否能够构成一个循环。

不可否认的,对于周期较短的一组数字这样找周期并不难,可是如果周期大到数百数千甚至数万时,靠这种方法找周期恐怕是杯水车薪。

当时我就迷茫在了这一长串的数字中不知所措,猛然想起前不久看过的KMP中next数组的性质,当即想到了用KMP求最小重复子串长度的方法,于是脑洞大开……

该性质为:令j=leni-next[i],如果i%j==0且i/j>1,j就是Pi的最小循环节( Pi表示文本串的前i个字符,leni表示该字符串的长度,一般表示为leni = i + 1)

关键代码如下:

复制代码
/*注:int next[]为next数组,int arr[]为要找规律的数组,len为数组长度*/
next[0] = 0;
for(int i = 1, q = 0; i < len; i++){
    while(q > 0 && arr[i] != arr[q])
        q = next[q-1];
    if (arr[i] == arr[q])
        q++;
    next[i] = q;
    if (q != 0 && (i + 1) % (i + 1 - q) == 0){
        printf("%d\n", i+1-q);
        break;
    }
}
复制代码

可以求出最小周期为42。

 

还有一种找规律的方法是直接对 f % 7 的值进行打表找规律,按照上述方法找到的周期为294,下面要做的就很简单了~

AC代码如下:

复制代码
 1 #include <cstdio>
 2 
 3 const char day[10][10] = {"Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"};
 4 int s[300];
 5 
 6 int work(int n)
 7 {
 8     int sum = 1;
 9     for(int i = 1; i <= n; i++){
10         sum = sum * n;
11         sum %= 7;
12     }
13     return sum;
14 }
15 
16 void init()
17 {
18     s[0] = 0;
19     for(int i = 1; i <= 294; i++){
20         s[i] = s[i-1] + work(i);
21         s[i] %= 7;
22     }
23 }
24 
25 int main()
26 {
27     int T;
28     int n;
29     init();
30     scanf("%d", &T);
31     while(T--){
32         scanf("%d", &n);
33         n %= 294;
34         printf("%s\n", day[ s[n]]);
35     }
36     return 0;
37 }
复制代码
ACM算法竞赛,字符串匹配和回文查是常见的问题类型,其KMP算法和Manacher算法是解决这类问题的重要工具。KMP算法全称为Knuth-Morris-Pratt算法,它通过预处理模式串来避免不必要的比较,显著提高了在主串的搜索效率。算法的核心在于构建一个部分匹配表(也称为“失配函数”或“next数组”),该表记录了模式串每个位置之前的子串有多少个字符与前缀重合。在实际搜索过程,一旦发生不匹配,算法就会利用这个表跳过尽可能多的字符,从而减少比较次数。 参考资源链接:[2018年更新:kuangbin分享的ACM算法模板及核心数学技巧](https://wenku.csdn.net/doc/6412b67fbe7fbd1778d46efd) 具体来说,KMP算法包含两个主要步骤:模式串预处理和模式匹配。在预处理阶段,计算部分匹配表;在匹配阶段,根据部分匹配表指导搜索过程,当模式串与主串在某个位置失配时,根据部分匹配表到模式串下一个可能匹配的位置,从而避免从头开始比较。KMP算法的时间复杂度为O(m+n),其m是模式串长度,n是主串长度,相比朴素的字符串匹配算法O(m*n),效率有显著提升。 Manacher算法则是用来查字符串最长回文子串的高效算法。与KMP算法类似,Manacher算法也是通过避免重复计算来提高效率的,但它使用了不同的策略。Manacher算法的核心思想是利用已知的回文信息来加速查新的回文子串。算法将字符串每个字符的左右两侧扩展,以跳过已经确定为回文的字符,并使用一个数组来记录已经到的回文半径,避免了复杂的边界条件判断。 在实现Manacher算法时,通常引入一个虚拟字符,将原始字符串转化为所有字符都在回文心的情况,这样每个字符都有一个对称的伴侣字符,算法通过维护一个数组来记录从每个位置出发能扩展的最大回文半径。Manacher算法的时间复杂度为O(n),其n是字符串的长度,这是它相较于其他回文查算法如心扩展法的优势所在。 在ACM竞赛,掌握KMP和Manacher算法对于处理字符串相关问题至关重要。通过《2018年更新:kuangbin分享的ACM算法模板及核心数学技巧》这一资料,你可以获得这些算法的具体实现和应用场景分析,加深对算法原理的理解,并在实际问题灵活运用,以达到事半功倍的效果。 参考资源链接:[2018年更新:kuangbin分享的ACM算法模板及核心数学技巧](https://wenku.csdn.net/doc/6412b67fbe7fbd1778d46efd)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值