zoukankan      html  css  js  c++  java
  • b_lg_最长前缀(kmp+dfs / 优化dp)

    如果一个集合P中的元素可以串起来(元素可以重复使用)组成一个序列s ,那么我们认为序列s可以分解为P中的元素。元素不一定要全部出现。举个例子,序列ABABACABAAB可以分解为下面集合中的元素:{A,AB,BA,CA,BBC} (BBC就没有出现)。求S符合条件的前缀的最大长度

    kmp思路:kmp求出每个段p[i]在s中出现的起始、结束位置,求完以后从0开始dfs枚举数组p能构成s的最长前缀

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e5+5, M=205;
    int tot,nx[20],ans,vis[N];
    string s,t[M],tmp;
    vector<int> pos[N];
    void get_nx(string& t) {
        for (int i=2,j=0,n=t.size()-1; i<=n; i++) {
            while (j && t[i]!=t[j+1]) j=nx[j];
            if (t[i]==t[j+1]) j++;
            nx[i]=j;
        }
    }
    void kmp(string& s, string& t) {
        int n=s.size()-1, m=t.size()-1;
        for (int i=1,j=0; i<=n; i++) {
            while (j && s[i]!=t[j+1]) j=nx[j];
            if (s[i]==t[j+1]) j++;
            if (j==m) {
                pos[i-j].push_back(i); //i看能和j相同,所以dfs从0开始
                j=nx[j];
            }
        }
    }
    void dfs(int i) { //i是s中的开头
        vis[i]=1;
        if (pos[i].empty() && i>ans) ans=i;
        for (int nx_p : pos[i]) if (!vis[nx_p]) {
            dfs(nx_p);
        }
    }
    int main() {
        std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
        while (cin>>tmp && tmp!=".") t[tot++]="/"+tmp;
        while (cin>>tmp) s+=tmp;
        s="/"+s;
        for (int i=0; i<tot; i++) {
            memset(nx,0,sizeof nx);
            get_nx(t[i]);
            kmp(s,t[i]);
        }
        dfs(0);
        cout<<ans;
        return 0;
    }
    

    发现有更优的线性dp解法:f[i]表示s的长度为i的前缀能否用p中的字符串组成
    暴力串匹配是对于每一个串都截取s长度相同的子串来比较,优化方法是:用数组len[i]来存储第i个片段的长度,先比较片段长度,再比较内容

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e5+5, M=205;
    int n,m,f[N],len[M];
    string s,t[M],tmp;
    int main() {
        std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
        while (cin>>tmp && tmp!=".") t[++m]=tmp, len[m]=tmp.size();
        while (cin>>tmp) s+=tmp;
        n=s.size(), f[0]=1;
        for (int i=1; i<=n; i++)
        for (int j=1; j<=m; j++) {
            if (i>=len[j] && f[i-len[j]] && s.substr(i-len[j], len[j])==t[j]) {
                f[i]=1;
            }
        }
        for (int i=n; ~i; i--) if (f[i])
            return cout<<i, 0;
        return 0;
    }
    
  • 相关阅读:
    重温.NET Remoting(四)
    asp.net mvc3的变态错误
    绝对定位与相对低位的应用
    Tsql script for Job
    Entlib5.0之数据查询
    Jquery Mobile dialog的生命周期
    关于委托Lamda表达式等的一个小例子
    网上摘录 数据分组处理
    Oracle 要点摘录
    [原創]另一種思路固定URL及.NET實現
  • 原文地址:https://www.cnblogs.com/wdt1/p/13867696.html
Copyright © 2011-2022 走看看