BZOJ 4974 字符串大师

做这个题废了好大的劲啊,想了差不多整整一天…知道自己挺菜的,昨天做完还是不怎么懂,看了别人的题解部分代码也没看懂。
然后模仿写的还tlm了。以至于怀疑别人代码的正确性…
今天做了个梦不知道为啥就开窍了,然后改了改竟然也ac了。
毕竟做了那么长时间的题,所以也想着总结一下。
列一下哪里不是很清楚,又怎么解决的。

在写一下概念。
*1、对于下标为0的next数组,next[ i ] 表示长度为 i 的字符串,公共前后缀长度为 next[ i ]。
2、题目给的 数组,就是那个(1 2 2 2 5) 我们称之为数组a ,其含义为长度为 i 的字符串最短循环节长度为 a[ i ]。
(比如样例的ababb,i = 3时,a[ 3 ] = 2,即字符串aba的最短循环节为ab)
又因为最短循环节等于为 len - next[len]。
这个不清楚的可以看这里,真的证明的挺好
https://www.cnblogs.com/chenxiwenruo/p/3546457.html
即a[len] = len - next[len]。
让他具有普遍性,就是 a[ i ] = i - next[ i ]。
根据这一个特点就可以求出next数组了。 next[ i ] = i - a[ i ]。

知道这些便有点方向,就好做了。

首先对于 a[ i ] != i 的情况,说明长度为 i 的字符串有最短循环节,所以第 i 个字母的位置与循环节的某一节字母的位置相等。怎么求呐?取余就好了。
举个例子,例如样例的ababb,i = 3时,a[ 3 ] = 2,那么第3个位置与3 % 2 = 1(取余)所以与第一个位置相等,而对于 i = 4,a[ 4 ] = 2。 4 % 2 = 0。到4循环节正好完整,4就等于循环节最后一个位置。

其次,我认为麻烦的是 a[ i ] == i 的时候。这个时候 next[ i ] = 0。说明长度为 i 的字符串没有公共前后缀(看概念1)。

然后对于 第 i - 1个位置,如果next[ i - 1] != -1。说明存在公共前后缀,例如样例求出的next[ 4 ] = 2,所以第5个位置就不能等于第3个位置。如果等于第3个位置那么next[ 5 ]就不再是0了而是3,这个画图也很容易想!所以把所有的next[ i - 1] + 1的字母全标记,与第i个位置不能一样,然后贪心从小遍历选最小的就好了

而我当初不懂的为什么是所有…
因为我只局限于这个长度为5的样例了…把字符串放大点。

对于第14的时候,next[ 14 ] = 0 。(14 == a[ 14 ])

而next[ 13 ] = 5,next[ 5 ] = 2 ,next[ 2 ] = 0 。(假设)

假设字符串 为abcabd…abcab(中间省略两个字母)

这时候14就不能等于第6个位置的字母、第3位置的字母、还有第1个位置的字母。即不能等于d 、 c 、a。

因为等于d时next[ 14 ] = 6。abcabdabcabd
等于c时next[ 14 ] = 3。abcabd…abcabc
等于a是next[ 14 ] = 1 。abcabd…abcaba
又晕的,在看一遍概念
对于下标为0的next数组,next[ i ] 表示长度为 i 的字符串,公共前后缀长度为 next[ i ]。

因为next[ 14 ] = 0。所以这些使 next [ 14 ] != 0的位置字母全标记为不能选。(这种思维他们说这是kmp next数组构造的逆过程),我不知道我这算不算。

看懂以上这些,就简单了。

下面是ac代码。

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

int  s[100000+66],a[100000+66],nxt[100000+66];
bool vis[33];

int main(){
    int n;
    cin >> n;
    nxt[0] = -1;
    for(int i = 1; i <= n; ++i){
        cin >> a[i];
        nxt[i] = i - a[i];
    }
    for(int i = 2; i <= n; ++i){
        if(a[i] != i) s[i] = s[i%a[i]== 0?a[i]:i%a[i]];
        else{

            memset(vis,false,sizeof(vis));
            int j = nxt[i-1];
            while(j != -1){
                vis[s[j+1]] = true;
                j = nxt[j];
            }
            for(int j = 1; j < 26; ++j)
            if(!vis[j]) {s[i] = j; break;}
        }
    }
    for(int i = 1; i <= n; ++i) printf("%c",s[i]+'a');
    cout << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44031744/article/details/86687860
今日推荐