第一眼:什么鬼东西ヾ(。`Д´。)


第二眼:显然,这道题要分段处理 类似[TJOI2018]碱基序列\ (建议做一做也是Hash+DP)\ 那你怎么第一眼没看出来


Hash处理+DP==AC


直接上代码 (码风奇特请谅解,注释还算详细)

#include<cstdio>
#include<cstring>
#define _for(i,a,b) for(register int i=(a);i<=(b);i=-~i)
#define __for(i,a,b) for(register int i=(a);i>=(b);i=~-i)
#define Re register int
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=2147483647,N=100000+20,M=11;
inline int re(){int x=0,f=0;
    char ch=getchar();
    while(ch>'9'||ch<'0')
        f|=ch=='-',ch=getchar();
    while(ch>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return f?-x:x;
}

char s1[N],s2[N];
ull hash1[110],hash2[N],p[N];
int len[110],n,cnt,hl,len2,num;
bool f[110][N];//懒得压维(按顺序处理到第几组、对应字符串中的哪一位)

int main()
{
    scanf("%s",s1+1);//从1开始存,个人习惯
    n=re();
    len[0]=strlen(s1+1);
    _for(i,1,len[0])//分段
        if(s1[i]=='*')//处理*和?
            hash1[++cnt]='*';
        else if(s1[i]=='?')
            hash1[++cnt]='?';
        else
            if(s1[i-1]<'a'||s1[i-1]>'z')//前面不是字母,记录长度
                hash1[++cnt]=s1[i]-'a'+1,len[cnt]=1;
            else//前面是字母
                hash1[cnt]=hash1[cnt]*131+s1[i]-'a'+1,len[cnt]++;
    hl=len[0];
    p[0]=1;//Hash 131进制
    _for(i,1,len[0])
        p[i]=p[i-1]*131;
    while(n--)
    {
        scanf("%s",s2+1);
        len2=strlen(s2+1);
        _for(i,1,len2)//正常Hash
            hash2[i]=hash2[i-1]*131+s2[i]-'a'+1;
        memset(f,0,sizeof(f));
        if(len2>hl)//小小的卡常
        {
            _for(i,hl+1,len2)
                p[i]=p[i-1]*131;
            hl=len2;
        }
        f[0][0]=1;//初始化!!!只初始化(0,0)
        _for(i,1,cnt)
        {
            if(!len[i])//不是字母
            {
                if(hash1[i]=='?')
                {//?是下一个字母任意
                    _for(j,0,len2)//由上一行向下转移
                        if(f[i-1][j])
                            f[i][j+1]=1;
                }
                else
                {//*是之后的字母全任意
                    _for(j,0,len2)
                        if(f[i-1][j])
                        {
                            _for(k,j,len2)
                                f[i][k]=1;
                            break;
                        }
                    if(f[i-1][0]) f[i][0]=1;
                }
                continue;
            }
            _for(j,len[i],len2)//正常字符串匹配
                if(hash1[i]==hash2[j]-hash2[j-len[i]]*p[len[i]]&&f[i-1][j-len[i]])
                    //一定是要能转移过来的
                    f[i][j]=1;
        }
        if(f[cnt][len2])//要求是完全匹配,所以只看放完最后一组最后一位是否能放
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}
/*
观察题目,通配符不超过10个
分段
Hash
跑DP
类似[TJOI2018]碱基序列
*/

 

 

posted on 2022-02-28 12:49  St_John  阅读(26)  评论(1编辑  收藏  举报