The North American Invitational Programming Contest 2018 E. Prefix Free Code(字典树+树状数组)

Consider nn initial strings of lower case letters, where no initial string is a prefix of any other initial string. Now, consider choosing kk of the strings (no string more than once), and concatenating them together. You can make this many such composite strings:

\displaystyle n \times (n - 1) \times (n - 2) \times . . . \times (n - k + 1)n×(n−1)×(n−2)×...×(n−k+1)

Consider sorting all of the composite strings you can get via this process in alphabetical order. You are given a test composite string, which is guaranteed to belong on this list. Find the position of this test composite string in the alphabetized list of all composite strings, modulo 10^9 + 7109+7. The first composite string in the list is at position 11.

Input Format

Each input will consist of a single test case.

Note that your program may be run multiple times on different inputs.

Each test case will begin with a line with two integers, first nn and then k (1 \le k \le n)k(1≤k≤n), where nn is the number of initial strings, and kk is the number of initial strings you choose to form composite strings. The upper bounds of nnand kk are limited by the constraints on the strings, in the following paragraphs.

Each of the next nn lines will contain a string, which will consist of one or more lower case letters a..za..z. These are the nn initial strings. It is guaranteed that none of the initial strings will be a prefix of any other of the initial strings.

Finally, the last line will contain another string, consisting of only lower case letters a..za..z. This is the test composite string, the position of which in the sorted list you must find. This test composite string is guaranteed to be a concatenation of kk unique initial strings.

The sum of the lengths of all input strings, including the test string, will not exceed 10^6106 letters.

Output Format

Output a single integer, which is the position in the list of sorted composite strings where the test composite string occurs. Output this number modulo 10^9 + 7109+7.

样例输入1

5 3
a
b
c
d
e
cad

样例输出1

26

样例输入2

8 8
font
lewin
darko
deon
vanb
johnb
chuckr
tgr
deonjohnbdarkotgrvanbchuckrfontlewin

样例输出2

12451

题目来源

The North American Invitational Programming Contest 2018

题意:n个单词,每一个单词都不是其它单词的前缀,又给出了由其中k个单词组成的一个大字符串,问这个大字符串是任选k个单词组成的字符串的所有情况按字典序排序是第几个?

思路:这道题思路是比较好想的,先把所有单词排序,标号,所以把给出的大字符串转换成了一个数列,因为每一个单词都不是其它单词的前缀,想到了KMP,觉得可能会超时,就没写,然后想到了map,存储这n个单词的时候遇到了困难,直接开char的二维数组会超内存,想到了vector<char>,写排序的时候卡住了,最后想干脆用string,超时就超时吧,转换为数列后还需维护每一个位置之前出现的比这个数小的数的个数,用树状数组和线段树都可以,string+map提交了一发,第4个数据就T了,当时觉得因为stringT了,看了标程是string+字典树,顿悟

#include <bits/stdc++.h>
#define lowbit(x) (x&-x)
using namespace std;
typedef long long ll;
const int MAXN = 1000006;
const ll MOD = 1e9 + 7;
string v[MAXN];
int C[MAXN];
int n;
int trie_n;
struct Trie_node
{
    int next[26];
    int index;

    Trie_node()
    {
        fill(next,next + 26,-1);
        index = -1;
    }
};
Trie_node trie[1000000];
void trie_insert(string s,int index)
{
    int root = 0;
    for(int i = 0; i < (int)s.length(); i++) {
        int next = trie[root].next[s[i] - 'a'];
        if(next == -1) {
            next = trie[root].next[s[i] - 'a'] = trie_n++;
        }
        root = next;
    }
    trie[root].index = index;
}
int getsum(int x)
{
    int ans=0;
    for(int i = x; i ; i -= lowbit(i))
        ans += C[i];
    return ans;
}
void update(int k,int x)
{
    for(int i = k;i <= n;i += lowbit(i))
        C[i] += x;
}
char test[MAXN];
int ans[MAXN];
ll fact[MAXN];
void init()
{
    fact[0] = 1;
    for (int i = 1; i <= MAXN; i++) {
        fact[i] = (fact[i-1] * i) % MOD;
    }
}

ll gcd(ll a, ll b, ll &s, ll &t)
{
  if(b == 0) { t = 0; s = (a < 0) ? -1 : 1; return (a < 0) ? -a : a;}
  else{ ll g = gcd(b, a % b, t, s);  t -= a / b * s;  return g; }
}
ll perm(int n, int r)
{
  if(n == 0) return 1;

  ll s, t;
  gcd(fact[n - r], MOD, s, t);

  ll ans = (fact[n] * s) % MOD;
  if(ans < 0) ans += MOD;

  return ans;
}
int main(void)
{
    int k;
    init();
    while(scanf("%d %d",&n,&k) != EOF) {
        trie_n = 1;
        for(int i = 1; i <= n; i++) {
            fill(trie[i].next,trie[i].next + 26 ,-1);
        }
        for(int i = 0; i < n; i++) {
            cin >> v[i];
        }
        sort(v,v + n);
        for(int i = 0; i < n; i++) {
            trie_insert(v[i],i + 1);
        }
        scanf("%s",test);
        int len = strlen(test);
        int node = 0;
        int cnt = 1;
        for(int i = 0; i < len; i++) {
            node = trie[node].next[test[i] - 'a'];
            if(trie[node].index != -1) {
                ans[cnt++] = trie[node].index;
                node = 0;
            }
        }
        ll aans = 1;
        memset(C,0,sizeof(C));
        for(int i = 1; i <= n; i++) {
            update(i,1);
        }
        for(int i = 1; i <= k; i++) {
            aans = (aans + (ll)(getsum(ans[i]) - 1) * (perm(n-i, k-i)) % MOD) % MOD;
            update(ans[i],-1);
        }
        printf("%lld\n",aans);
    }
    return 0;
}
/*
8 8
font
lewin
darko
deon
vanb
johnb
chuckr
tgr
deonjohnbdarkotgrvanbchuckrfontlewin
*/

标程 ,树状数组看的我一愣一愣的

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const ll MOD = 1000000007;

ll fact[200001];

ll gcd(ll a, ll b, ll &s, ll &t) { // a*s+b*t = g
  if (b==0) { t = 0; s = (a < 0) ? -1 : 1; return (a < 0) ? -a : a;
  } else { ll g = gcd(b, a%b, t, s);  t -= a/b*s;  return g; }
}

ll perm(int n, int r)
{
  if (n == 0) return 1;

  ll s, t;
  gcd(fact[n-r], MOD, s, t);
  
  ll ans = (fact[n]*s) % MOD;
  if (ans < 0) ans += MOD;


  return ans;
}

struct trie_node
{
  int next[26];
  int index;

  trie_node()
  {
    fill(next, next+26, -1);
    index = -1;
  }
};

int trie_n = 1;
trie_node trie[1000000];

void trie_insert(string s, int index)
{
  int root = 0;
  
  for (int i = 0; i < (int)s.length(); i++) {
    int next = trie[root].next[s[i]-'a'];
    if (next == -1) {
      next = trie[root].next[s[i]-'a'] = trie_n++;
    }
    root = next;
  }
  trie[root].index = index;
}

class FenwickTree{  // All entries must be >= 0 even after decrement
public:             // Every function is O(log n)
  FenwickTree(int n) : N(n), iBM(1), tree(n,0) {
    while (iBM < N) iBM *= 2;
  }
  
  // inc/dec the entry at position idx by val
  void incEntry(int idx, int val) {
    do tree[idx] += val; while(idx && (idx += (idx & (-idx))) < N);
  }
  
  // return the cumulative sum val[0] + val[1] + ... + val[idx]
  int cumulativeSum(int idx) const {
    int sum = tree[0];
    for( ; idx > 0 ; idx &= idx-1) sum += tree[idx];
    return sum;
  }
  
  // return the entry indexed by idx
  int getEntry(int idx) const {
    int val = tree[idx], par = idx & (idx-1);
    if (idx--) for( ; par != idx ; idx &= idx-1) val -= tree[idx];
    return val;
  }

  // return the largest index such that the cumulative frequency is
  // what is given, or -1 if it is not found
  int getIndex(int sum) const {
    if ((sum -= tree[0]) < 0) return -1;
    int idx = 0;
    for(int bM = iBM ; bM != 0 && idx < N-1 ; bM >>= 1)
      if (sum >= tree[idx+bM]) sum -= tree[idx += bM];
    return (sum != 0) ? -1 : min(N-1,idx);
  }
  
private:
  int N, iBM; vector<int> tree;
};


int main()
{
  int n, k;
  cin >> n >> k;

  fact[0] = 1;
  for (int i = 1; i <= n; i++) {
    fact[i] = (fact[i-1]*i) % MOD;
  }
  
  vector<string> v(n);
  for (int i = 0; i < n; i++) {
    cin >> v[i];
  }
  sort(v.begin(), v.end());

  for (int i = 0; i < n; i++) {
    trie_insert(v[i], i);
  }
  
  string s;
  cin >> s;

  vector<int> A;
  int node = 0;
  for (int i = 0; i < (int)s.length(); i++) {
    node = trie[node].next[s[i]-'a'];
    if (trie[node].index != -1) {
      A.push_back(trie[node].index);
      node = 0;
    }
  }

  FenwickTree ft(n);
  for (int i = 0; i < n; i++) {
    ft.incEntry(i, 1);
  }

  ll ans = 1;
  for (int i = 0; i < k; i++) {
    ans += (ft.cumulativeSum(A[i])-1) * perm(n-i-1, k-i-1) % MOD;
    ans %= MOD;
    ft.incEntry(A[i], -1);
  }
  
  cout << ans << endl;
  return 0;
}

猜你喜欢

转载自blog.csdn.net/GYH0730/article/details/81571314