• P2292 [HNOI2004]L语言


    传送门

    思路:

       毒瘤的字典树!

    ▲主要分有两个步骤:

      ① 日常的建树。

      ② 暴力地求解。

    ▲日常建树:过于基础,跳过。

    ▲重点在于如何暴力地求解而不被卡掉(DP?不存在的

      可以利用区间动规的思想,枚举文章的左右端点,判断中间区间的文章是否可以被理解。

      文章的长度几乎为 INF ,直接 N2 枚举 1~INF 显然是不可能的。

      而看到字典里的单词长度(模式串)长度只有 10 以下,这为暴力枚举提供了一条出路。

    先看一段暴力求解的代码:

    for(LL j=0;j<len;j++)//很暴力的枚举文章的右端点
        for(LL k=max(j-lenth_max,-INF);k<=j;k++)//枚举文章的区间左端点,(有k=max(j-lenth_max,-INF)每次最多只有枚举10个单位长度)。
            if((k==-INF||f[k])&&(find(k+1,j)))
            {
                f[j]=true;ans=j+1;break;
            }//注-INF = -1

      find( i , j ) 用于查找 i~j 的文章中,是否能够成功匹配单词。 

      设一个 f[ j ] 表示整篇文章中,k ~ j 的文章区间能否被理解,同时要求之前文章的 f[ k ] 也要能够被理解才能更新 ans 。( 因为要求求解能够被理解的文章最长前缀 ,不能断开 )。

      为了能够更加优化暴力,lenth_max 记录最长的单词长度。

      有个小细节:在匹配成功一段区间后的 break ,跳出的只是枚举左端点的循环,而外层的枚举右端点的循环仍然继续。

    标程:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<string>
    #include<cstdlib>
    #include<stack>
    #include<vector>
    #include<queue>
    #include<deque>
    #include<map>
    #include<set>
    using namespace std;
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define maxn 1000002
    #define INF 1
    typedef long long LL;
    LL n,m,len,lenth_max,ans,ch[maxn>>6][26],cnt;
    char s[maxn];
    bool f[maxn],bo[maxn>>6];
    inline LL read()
    {
        LL kr=1,xs=0;
        char ls;
        ls=getchar();
        while(!isdigit(ls))
        {
            if(!(ls^45))
                kr=-1;
            ls=getchar();
        }
        while(isdigit(ls))
        {
            xs=(xs<<1)+(xs<<3)+(ls^48);
            ls=getchar();
        }
        return xs*kr;
    }
    inline void insert(char *s)
    {
        LL u=0,len=strlen(s);
        lenth_max=max(len,lenth_max);//记录字典里最长的单词长度 
        for(LL i=0;i<len;i++)
        {
            LL c=s[i]-'a';
            if(!ch[u][c]) ch[u][c]=++cnt;
            u=ch[u][c];
        }
            bo[u]=1;
    }
    inline bool find(LL l,LL r)
    {
        LL u=0;
        for(LL i=l;i<=r;i++)
        {
            LL c=s[i]-'a';
            if(!ch[u][c]) return false;
            u=ch[u][c];
        }
            return bo[u];
    }
    int main()
    {
        freopen("L.in","r",stdin);
        freopen("L.out","w",stdout);
        n=read();m=read();
        for(LL i=1;i<=n;i++)
        {
            scanf("%s",s);
            insert(s);
        }
        for(LL i=1;i<=m;i++)
        {
            memset(f,false,sizeof(f));ans=0;
            scanf("%s",s);
            LL len=strlen(s);
            for(LL j=0;j<len;j++)
                for(LL k=max(j-lenth_max,-INF);k<=j;k++)
                    if((k==-INF||f[k])&&(find(k+1,j)))
                    {
                        f[j]=true;ans=j+1;break;
                    }
            printf("%lld
    ",ans);
        }
    return 0;
    }
  • 相关阅读:
    6-1
    5-9
    5-8
    5-7
    5-6
    实验4-1:掌握Android应用调试方法、添加新界面
    实验3:理解Activity 的生命周期
    R.java常见问题解决方案
    配置Android模拟器
    第02周实验: 变量、算术运算和数据类型
  • 原文地址:https://www.cnblogs.com/lck-lck/p/9813562.html
Copyright © 2020-2023  润新知