抄卡组(HNOI2014)

抄卡组(HNOI2014)

题目描述

给出\(n\)个字符串,其中含有通配符\(*\),通配符可以为长度大于等于\(0\)的任意字符串。要求判断这\(n\)个字符串是否有可能为同一个字符串。

输入

第一行包含一个正整数 \(T\),表示了数据组数。
接下来包含 \(T\) 组数据:
每组数据的第一行是一个正整数 \(N\),表示该组数据的字符串个数。
接下来 \(N​\) 行,每行一个字符串,字符串仅包含小写字母、数字、通配符 *

思路

这题有个比较玄学的结论:如果该n个字串的前后缀都能够匹配(即第一个/最后一个通配符之前/之后的部分),且中间至少有一个通配符,那么肯定是可以满足的。此处不给出详细证明,应该感性思考一下,都是没有问题的。

那么计算前后缀的规则比较简单(复杂):如果当前的前缀匹配超出了标准前缀,那么就把标准前缀更新到当前前缀,若匹配失败则返回\(0​\). 后缀同理即可。

bool get(vector<char>str){
    int i=0;
    len=(int)str.size();
    while(i<len&&str[i]!='*'){
        if(i>(int)pre.size()-1)pre.pb(str[i]);
        else if(pre[i]!=str[i])return false;
        i++;
    }
    i=0;
    while(i<len&&str[len-i-1]!='*'){
        if(i>(int)suf.size()-1)suf.pb(str[len-i-1]);
        else if(str[len-i-1]!=suf[i])return false;
        i++;
    }
    return true;
}

接下来就是存在没有通配符的字串的情况,这个时候其实就是把一个没有通配符的串拿出来(这个串就相当于最终卡组了),此处叫它为\(p\)串,那么只要拿另外的\(n-1\)个串与他进行\(KMP\)匹配即可。

怎么\(KMP\)呢?只要把所有\(n-1\)个串按通配符分成若干个子串,然后只要在不改变每个子串的先后顺序,在\(p\)中找到对应的子串即可。

abaaab

*ba*a*

如以上两串,若第一个为\(p\)串,那么以下的两个子串 \(ba\)\(a\),可以分别在\(p\)串中得到匹配,同时匹配的先后顺序保持不变。

不过同时,也要满足前后缀匹配(其实也可以在\(KMP\)中特判前后缀,但个人感觉比较麻烦),可以拿第一种情况方法照搬过来,为什么要匹配前后缀呢?拿这组数据就知道了\(abaaab\),\(ba*ab\),虽然能匹配但不合法。

那么综合以上两种情况就得到最后解发了,总结一下就是:判前缀后缀,若有无通配符的字串,就再将每个其他字符串再KMP匹配。上代码(奇丑无比)

代码

#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l,i##R=r;i<=i##R;i++)
#define DOR(i,r,l) for(int i=r,i##L=l;i>=i##L;i--)
#define loop(i,n) for(int i=0,i##R=n;i<i##R;i++)
#define sf scanf
#define pf printf
#define mms(a,x) memset(a,x,sizeof a)
#define pb push_back
using namespace std;
typedef long long ll;
typedef long double db;
template<typename A,typename B>inline void chkmax(A &x,const B y){if(x<y)x=y;}
template<typename A,typename B>inline void chkmin(A &x,const B y){if(x>y)x=y;}
const int N=1e5+5;
int n;
vector<char>str[N],pre,suf;
bool legal(char c){return (c>='a'&&c<='z')||(c>='0'&&c<='9')||c=='*';}
bool read(int x){
    char c;bool flag=0;str[x].clear();
    while(c=getchar(),!legal(c));
    str[x].pb(c),flag|=(c=='*');
    while(c=getchar(),legal(c))str[x].pb(c),flag|=(c=='*');
    return flag;
}
int len;
bool fpre,fsuf;
bool get(vector<char>str){
    int i=0;
    len=(int)str.size();
    while(i<len&&str[i]!='*'){
        if(i>(int)pre.size()-1){
            pre.pb(str[i]);
        }
        else if(pre[i]!=str[i])return false;
        i++;
    }
    i=0;
    while(i<len&&str[len-i-1]!='*'){
        if(i>(int)suf.size()-1){
            suf.pb(str[len-i-1]);
        }
        else if(str[len-i-1]!=suf[i])return false;
        i++;
    }
    return true;
}
struct Pt2{
    int Next[N*20];
    int lst,id;
    char tmp[N*20];
    void get_nxt(char str[],int len){
        int j=0;
        FOR(i,2,len){
            while(j&&str[i]!=str[j+1])j=Next[j];
            j+=(str[i]==str[j+1]);
            Next[i]=j;
        }
    }
    bool KMP(int len){
        int j=0;
        FOR(i,lst,str[id].size()-1){
            while(j&&str[id][i]!=tmp[j+1])j=Next[j];
            j+=(str[id][i]==tmp[j+1]);
            if(j==len){lst=i+1;return 1;}
        }
        return 0;
    }
    bool check(vector<char>str){
        lst=0;
        loop(i,str.size()){
            if(str[i]!='*'){
                int ct=0;
                while(i<(int)str.size()&&str[i]!='*')tmp[++ct]=str[i],i++;
                tmp[ct+1]='\0';
                get_nxt(tmp,ct);
                if(!KMP(ct))return false;
                i--;
            }
        }
        return true;
    }
    void solve(){
        id=0;
        sf("%d",&n);
        FOR(i,1,n)if(!read(i))id=i;
        fpre=fsuf=0;
        pre.clear(),suf.clear();
        bool flag=0;
        FOR(i,1,n)if(!flag&&!get(str[i]))flag=1;
        if(flag){puts("N");return;}
        if(id){
            FOR(i,1,n){
                if(i==id)continue;
                if(!check(str[i])){puts("N");return;}
            }
        }
        puts("Y");
    }
}Pt_2;
int main(){
    freopen("hs.in","r",stdin);
    freopen("hs.out","w",stdout);
    int T;
    sf("%d",&T);
    while(T--)Pt_2.solve();
    return 0;
}

转载于:https://www.cnblogs.com/Heinz/p/10732126.html

根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值