数据结构专题

2019/7/15,重新开始数据结构的训练,这是记录,从字符串开始。

附刘汝佳白书题库:https://vjudge.net/article/61

前缀树:

https://www.cnblogs.com/luosongchao/p/3239521.html

https://www.cnblogs.com/justinh/p/7716421.html

https://www.cnblogs.com/grandyang/p/4491665.html

刘汝佳源码模板:

// 字母表为全体小写字母的Trie
struct Trie {
  ll ch[maxnode][sigmansize];
  ll val[maxnode];
  ll sz; // 结点总数
  void clear() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); } // 初始时只有一个根结点
  ll idx(char c) { return c - 'a'; } // 字符c的编号

  // 插入字符串s,附加信息为v。注意v必须非0,因为0代表“本结点不是单词结点”
  void insert(const char *s, ll v) {
    ll u = 0, n = strlen(s);
    for(ll i = 0; i < n; i++) {
      ll c = idx(s[i]);
      if(!ch[u][c]) { // 结点不存在
        memset(ch[sz], 0, sizeof(ch[sz]));
        val[sz] = 0;  // 中间结点的附加信息为0
        ch[u][c] = sz++; // 新建结点
      }
      u = ch[u][c]; // 往下走
    }
    val[u] = v; // 字符串的最后一个字符的附加信息为v
  }

  // 找字符串s的长度不超过len的前缀
  void findnprefixes(const char *s, ll len, vector<ll>& ans) {
    ll u = 0;
    for(ll i = 0; i < len; i++) {
      if(s[i] == '\0') break;
      ll c = idx(s[i]);
      if(!ch[u][c]) break;
      u = ch[u][c];
      if(val[u] != 0) ans.pushnback(val[u]); // 找到一个前缀
    }
  }
};

白书例题好像交不了,附上源码吧。。代码学习了一晚上

题目:P209 LA3942

// LA3942 Remember the Word
// Rujia Liu
#include<algorithm>
#include <iostream>
#include  <sstream>
#include  <cstring>
#include  <cstdlib>
#include   <string>
#include   <vector>
#include   <cstdio>
#include   <math.h>
#include    <queue>
#include    <stack>
#include      <set>
#include      <map>
typedef long long ll;
typedef long double ld;
typedef unsigned long long uint;
#define SZ(x) ((int)x.size())
const double pi = acos(-1);
#define rep(i,a,n) for (int i = a; i < n; ++i)
#define per(i,a,n) for (int i = n-1; i >= a; --i)
#define devil ios::sync_with_stdio(false)
#define cim constexpr int maxn = 
#define PAUSE system("pause")
const int inf = 0x3f3f3f3f;
using namespace std;
//head

const int maxnode = 4000 * 100 + 10;
const int sigmansize = 26;

// 字母表为全体小写字母的Trie
struct Trie {
    int ch[maxnode][sigmansize];
    int val[maxnode];
    int sz; // 结点总数
    void clear() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); } // 初始时只有一个根结点
    int idx(char c) { return c - 'a'; } // 字符c的编号

    // 插入字符串s,附加信息为v。注意v必须非0,因为0代表“本结点不是单词结点”
    void insert(const char *s, int v) {
        int u = 0, n = strlen(s);
        for (int i = 0; i < n; i++) {
            int c = idx(s[i]);
            if (!ch[u][c]) { // 结点不存在
                memset(ch[sz], 0, sizeof(ch[sz]));
                val[sz] = 0;  // 中间结点的附加信息为0
                ch[u][c] = sz++; // 新建结点
            }
            u = ch[u][c]; // 往下走
        }
        val[u] = v; // 字符串的最后一个字符的附加信息为v
    }

    // 找字符串s的长度不超过len的前缀
    void findnprefixes(const char *s, int len, vector<int>& ans) {
        int u = 0;
        for (int i = 0; i < len; i++) {
            if (s[i] == '\0') break;
            int c = idx(s[i]);
            if (!ch[u][c]) break;
            u = ch[u][c];
            if (val[u] != 0) ans.push_back(val[u]); // 找到一个前缀
        }
    }
};

const int maxl = 300000 + 10; // 文本串最大长度
const int maxw = 4000 + 10;   // 单词最大个数
const int maxwl = 100 + 10;   // 每个单词最大长度
const int MOD = 20071027;

int d[maxl], len[maxw], S;
char text[maxl], word[maxwl];
Trie trie;

int main() {
    int kase = 1;
    while (scanf("%s%d", text, &S) == 2) {
        trie.clear();
        for (int i = 1; i <= S; i++) {
            scanf("%s", word);
            len[i] = strlen(word);
            trie.insert(word, i);
        }
        memset(d, 0, sizeof(d));
        int L = strlen(text);
        d[L] = 1;
        for (int i = L - 1; i >= 0; i--) {
            vector<int> p;
            trie.findnprefixes(text + i, L - i, p);
            for (int j = 0; j < p.size(); j++)
                d[i] = (d[i] + d[i + len[p[j]]]) % MOD;
        }
        printf("Case %d: %d\n", kase++, d[0]);
    }
    return 0;
}

VJ刷不了改刷XLS博客,估计后面的实战会去刷牛客训练题

xls博客题目链接:https://blog.csdn.net/ccsu_cat/article/details/77942185

 

#include<bits/stdc++.h>
using namespace std;

const int maxnode = 4000 * 100 + 10;
const int sigmansize = 26;
const int maxn = 4e5 + 5;
const int maxl = 15;
int maxa = 0;
char maxstr[maxl];
// 字母表为全体小写字母的Trie
struct Trie {
    int ch[maxnode][sigmansize];
    int val[maxnode];
    int sz; // 结点总数
    void clear() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); } // 初始时只有一个根结点
    int idx(char c) { return c - 'a'; } // 字符c的编号
 
    // 插入字符串s,附加信息为v。注意v必须非0,因为0代表“本结点不是单词结点”
    void insert(const char *s, int v) {
        int u = 0, n = strlen(s);
        for (int i = 0; i < n; i++) {
            int c = idx(s[i]);
            if (!ch[u][c]) { // 结点不存在
                memset(ch[sz], 0, sizeof(ch[sz]));
                val[sz] = 0;  // 中间结点的附加信息为0
                ch[u][c] = sz++; // 新建结点
            }
            u = ch[u][c]; // 往下走
        }
        val[u] += v; // 字符串的最后一个字符的附加信息为v
        if(val[u] > maxa)
        {
            //cout << s << "      " << maxa << endl;
            strcpy(maxstr, s);
            maxa = val[u];
        }
    }
    // 找字符串s的长度不超过len的前缀
    void findnprefixes(const char *s, int len, vector<int>& ans) {
        int u = 0;
        for (int i = 0; i < len; i++) {
            if (s[i] == '\0') break;
            int c = idx(s[i]);
            if (!ch[u][c]) break;
            u = ch[u][c];
            if (val[u] != 0) ans.push_back(val[u]); // 找到一个前缀
        }
    }
}trie;
char str[maxn][maxl];
int main()
{
    int n;
    scanf("%d", &n);
    for(int i  = 1; i <= n; i++)
    {
        scanf("%s", &str[i]);
        trie.insert(str[i], 1);
    }
    cout << maxstr << " ";
    cout << maxa << endl;
    return 0;
}

牛客题库链接:https://ac.nowcoder.com/acm/skill/detail/acm/1269

2019/7/17

A  Equivalent Prefixes https://ac.nowcoder.com/acm/contest/881/A

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 2e5 + 10, mod = 998244353;
int a[maxn], b[maxn], l[2][maxn], r[2][maxn], rt[2], sz[2][maxn];
stack<int> s;
int dfs(int u) {
    if (l[0][u] != l[1][u] || r[0][u] != r[1][u])
        return 0;
    int ok = 1;
    if (l[0][u])
        ok &= dfs(l[0][u]);
    if (r[0][u])
        ok &= dfs(r[0][u]);
    return ok;
}
int gao(int n) {
    for (int i = 1; i<= n; i++) {// 建立a数组的笛卡尔树
        l[0][i] = r[0][i] = 0;
        while (!s.empty() && a[i] < a[s.top()])
            l[0][i] = s.top(), s.pop();
        if (!s.empty())
            r[0][s.top()] = i;
        s.push(i);
    }
    while (!s.empty())
        rt[0] = s.top(), s.pop();
    for (int i = 1; i <= n; i++) {// 建立b数组的笛卡尔树
             l[1][i] = r[1][i] = 0;
            while (!s.empty() && b[i] < b[s.top()])
                l[1][i] = s.top(), s.pop();
            if (!s.empty())
                r[1][s.top()] = i;
            s.push(i);
    }
    while (!s.empty())
        rt[1] = s.top(), s.pop();
    if (rt[0] != rt[1])
        return 0;
    return dfs(rt[0]);//判断两颗笛卡尔树是否一致
}
int main(){
    int n;
    while (~scanf("%d", &n)) {
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        for (int i = 1; i <= n; i++)
            scanf("%d", &b[i]);
        int L = 1, R = n;
        while (L < R) {
            int m = (L + R) / 2;
            if (gao(m))
                L = m + 1;
            else
                R = m;
        }
        if (!gao(L))
            L--;
        printf("%d\n", L);
    }
}

2019\7\19

学习一波线段树:

复杂度n * m log(n)非常极限

#include<bits/stdc++.h>
#define pi pair<int, int>
#define mk make_pair
using namespace std;
const int maxn = 1010;
char s[maxn][maxn];
int h[maxn][maxn], vis[maxn * maxn], mn[maxn * 4], L[maxn], R[maxn];
#define ls o * 2    
#define rs o * 2 + 1
#define mid (l + r) / 2
void up(int o, int l, int r, int k, int v) {
    if (l == r) {
        mn[o] = v;
        return;
    }
    if (k <= mid)
        up(ls, l, mid, k, v);
    else
        up(rs, mid + 1, r, k, v);
    mn[o] = min(mn[ls], mn[rs]);
}
int qu(int o, int l, int r, int v) {
    if (mn[o] >= v)
        return r + 1;
    if (l == r)
        return l;
    if (mn[ls] < v)
        return qu(ls, l, mid, v);
    return qu(rs, mid + 1, r, v);
}
int qu2(int o, int l, int r, int v) {
    if (mn[o] >= v)
        return l - 1;
    if (l == r)
        return l;
    if (mn[rs] < v)
        return qu2(rs, mid + 1, r, v);
    return qu2(ls, l, mid, v);
}
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%s", s[i] + 1);
        for (int j = 1; j <= m; j++) {
            if (s[i][j] == '0')
                h[i][j] = 0, up(1, 1, m, j, 0);
            else {
                h[i][j] = h[i - 1][j] + 1;
                up(1, 1, m, j, h[i][j]);
            }
        }
        for (int j = 1; j <= m; j++)
            if (h[i][j]) {
                int k = qu(1, 1, m, h[i][j]);
                R[j] = k;
                up(1, 1, m, j, 1e9);
            }
            else
                up(1, 1, m, j, 1e9);
 
        for (int j = 1; j <= m; j++)
            up(1, 1, m, j, h[i][j]);
 
        map<pi, int> mp;
        for (int j = m; j; j--)
            if (h[i][j]) {
                int k = qu2(1, 1, m, h[i][j]);
                int S = h[i][j] * (R[j] - k - 1);
                if (!mp[mk(k, h[i][j])]) {
                    mp[mk(k, h[i][j])] = 1;
                    vis[S]++;
                    S = (h[i][j] - 1) * (R[j] - k - 1);
                    vis[S]++;
                    S = h[i][j] * (R[j] - k - 2);
                    vis[S]++;
                }
                up(1, 1, m, j, 1e9);
            }
            else
                up(1, 1, m, j, 1e9);
    }
    int ans = 0, p = 2;
    for (int i = n * m; i; i--)
    if (vis[i] >= p) {
        ans = i;
        break;
    }
    else
        p -= vis[i];
    printf("%d\n", ans);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值