征求hack 或 证明正确性
查看原帖
征求hack 或 证明正确性
538609
Neutralized楼主2022/1/26 21:52

大家好,我花了一晚上用 O(mST)O(m\,|S|\,|T|) 艹过了本题
但是我不知道它是否还能被hack(

具体来说,这是一个又丑又长的投机做法
先对模式串建立Trie,存下每个串的结尾节点对应的串长,然后构建AC自动机
对每个询问串,开一个bitset当桶
用于存储当前串的每个位置是否可以当作单词分割点并且保证前缀有效,即从某个点断开之后前缀是否一定有效
然后,就直接大力跳fail统计,如果这一点的tag可以对应到之前的某个断点,即从之前的某个断点可以连接一个新单词拓展到这个点,就把连接之后的长度也标为可达并且更新最右端点
最后答案就是最右边的端点

然后这样朴素的做法就T掉了最后两个点.

但是,凭借多年被卡暴力 被毒瘤数据阴的经验,我猜最后两个点是 针对fail[x] 而设计的,目的就是把本来能跳很多的fail卡成只能往父亲节点走
那么有可能Trie大体上是一条链,
有可能是aaaaaaaaaaaaaaaaaaaaaaa...aaab这种标准卡 n2n^2 的数据
而在这样的数据上,更新答案其实就没必要考虑fail的贡献,直接一条路往下更新就可以了
所以就打了下面的那个getans2()
然后
就过了……

所以这种看起来就很假的做法是否还可以hack?
如果可以,那么hack数据是什么样的?
如果不能,那么正确性怎么证明?

求巨佬解答

code:

#include <bits/stdc++.h>
#define ri register int
#define MAXN 401
#define g() getchar()
#define pc(a) putchar(a)
#define Tp template<typename T>
using namespace std;

namespace SlowIO
{
	Tp inline void rd(T &x)	{
		x=0;char i=g();bool f=1;
		while(!isdigit(i)) f&=(i!='-'),i=g();
		while(isdigit(i)) x=(x<<3)+(x<<1)+(i^48),i=g();
		x*=((f<<1)-1);
		}
	Tp inline void op(T x){
		if(x<0) pc('-'),x=-x;
		if(x>=10) op(x/10);
		pc(x%10+48);
		}
	Tp inline void writeln(T x){op(x);pc('\n');}
	Tp inline void writesp(T x){op(x);pc(' ');}
	Tp inline void writespf(T x){pc(' ');op(x);}
};
using namespace SlowIO;

int n,m,tot;
string s,t;
int trie[MAXN][26];
int fail[MAXN],tag[MAXN];
inline void insert(string s){
    ri ptr=0,len=s.length();
    for(ri i=0;i<len;i++){
        ri t=s[i]-'a';
        if(!trie[ptr][t])
        trie[ptr][t]=++tot;
        ptr=trie[ptr][t];
    }
    tag[ptr]=len;
}
int q[MAXN],l=1,r;
inline void getfail(){
    for(ri i=0;i<26;i++)
    if(trie[0][i]) q[++r]=trie[0][i];
    while(l<=r){
        int u=q[l++];
        for(ri i=0;i<26;i++)
        if(trie[u][i])
        fail[trie[u][i]]=trie[fail[u]][i],
        q[++r]=trie[u][i];
        else
        trie[u][i]=trie[fail[u]][i];
    }
}
bitset<2000001> cnt;
static int Mxx;
inline int getans(string s){
    ri ptr=0,len=s.length();
    cnt.reset(),Mxx=0,cnt[0]=1;
    for(ri i=0;i<len;i++){
        ptr=trie[ptr][s[i]-'a'];
        for(ri t=ptr;t;t=fail[t])
        if(tag[t]&&cnt[i+1-tag[t]])
        	cnt[i+1]=1,Mxx=max(Mxx,i+1);
    } return Mxx;
}
inline int getans2(string s){
    ri ptr=0,len=s.length();
    cnt.reset();
    Mxx=0,cnt[0]=1;
    for(ri i=0;i<len;i++){
        ptr=trie[ptr][s[i]-'a'];
        if(tag[ptr]&&cnt[i+1-tag[ptr]])
        cnt[i+1]=1,Mxx=max(Mxx,i+1);
    } return Mxx;
}

int main()
{
    rd(n),rd(m); ios::sync_with_stdio(false),cin.tie(NULL);
    for(ri i=1;i<=n;i++)
    cin>>s,insert(s);
    getfail(); static int lim=1000000;
    while(m--){
        cin>>t;
        if(t.length()>lim)
        writeln(getans2(t));
        else writeln(getans(t));
    }
    return 0;
}

另外,虽然过了本题,但这种投机做法完全看脸
以后是不会用了除非没办法

明天去学习正解……

2022/1/26 21:52
加载中...