2018蓝桥杯省赛准备

ACM代码整合

  • ACM代码整合
      • !!!特别提醒
      • 1、最小公倍数gcd
      • 2、最大公约数lcm
      • 3、Fibonacci数列
      • 4、memset初始化
      • 5、fill初始化
      • 6、ctype.h头文件
      • 7、素数系列
      • 8、BFS系列
      • 9、DFS系列
      • 10、动态规划(01背包,完全背包)
      • 11、并查集
      • 12、树状数组
      • 13、快速幂 and 矩阵快速幂
      • 14、最短路
      • 15、string
      • 16、二分搜索
      • 17、字符串函数
      • 18、set & multiset
      • 19、map & multimap
      • 20、queue & priority_queue
      • 21、stack
      • 22、vector
      • 23、贪心算法
      • 24、数制转换
      • 25、LCS
      • 26、LIS
      • 27、最大字段和
      • 28、循环数组最大字段和
      • 29、最小正字段和
      • 30、大数运算
      • 31、Trie

如果发现文章中有不正确的地方,欢迎联系本人,感谢指出问题的每个人

PS:本文一切代码均有C/C++编写,参与JAVA组的人可以理解思想了以后尝试改写

!!!特别提醒

1、注意程序最后应有返回值,且返回值为0

2、万能头文件可以使用(bits/stdc++.h),比赛中使用c++的方法是,记得开全局空间using namespace std;

3、熟悉STL用法,包括vector、set、map、pair等结构的用法,这会给你剩下许多时间

4、例如max_element、lower_bound函数的用法请熟悉

5、在不能保证自己的算法的绝对正确性的情况下,可以选择保住现有的分,而不去尝试难度较高且自己没有把握的方法

1、最小公倍数gcd

int gcd(int a,int b){
    return b == 0 ? a : gcd(b,a%b);
} 

2、最大公约数lcm

int gcd(int a,int b){
    return b == 0 ? a : gcd(b,a%b);
} 

int lcm(int a,int b){
    return a*b/gcd(a,b);
}

3、Fibonacci数列

采用记忆化搜索的写法,或者使用状态转移的方法推导

  • 记忆化搜索写法
int dp[1005];
int dfs(int n){
    if(n==1 || n==2){
        return 1;
    }
    if(dp[n] > 0){
        return dp[n];
    }else{
        dp[n] = dfs(n-1) + dfs(n-2);
        return dp[n];
    }   
}
  • 状态转移写法
int dp[1000];
dp[1] = 1;
dp[2] = 1;
for(int i=3;i<1000;i++){
  dp[i] = dp[i-1] + dp[i-2];
} 

4、memset初始化

  • memset在整形数组中推荐两种用法
int dp[1005];
memset(dp,0,sizeof(dp));//dp中的值全部置为了0
memset(dp,-1,sizeof(dp));//dp中的值全部置为了-1

有人问1,2,3其他的可行,当然是不行的,至于原因有兴趣想要知道的话,可以在网上了解

  • memset在char数组中
char str[1005];
memset(str,0,sizeof(str));

这样就是将char数组重置了,在多组输入的情况中,memset将会是一个非常高效的方法

5、fill初始化

int dp[100];
fill(dp,dp+10,5);

此时dp[0] -> dp[9]的值全部被置为了5

char str[100];
fill(str,str+10,'m');

此时str[0] -> str[9]的值全部被置为了m

以上两个例子相信大家一下子就对fill有所了解了吧,在实际运用中大家可以根据自己的需要使用fill和memset

6、ctype.h头文件

函数名称 函数原型 函数功能 函数返回
isalpha int isalpha(char ch) 检查ch是否是字母 是字母返回非0(在vs2015中为2),否则返回 0
iscntrl int iscntrl(int ch) 检查ch是否控制字符(其ASCII码在0和0x1F之间,数值为 0-31) 是返回非0,否则返回 0
isdigit int isdigit(char ch) 检查ch是否是数字(0-9) 是返回非0,否则返回0
isgraph int isgraph(int ch) 检查ch是否可显示字符(其ASCII码在0x21到0x7E之间),不包括空格 是返回非0,否则返回0
islower int islower(int ch) 检查ch是否小写字母(a-z) 是返回非0,否则返回0
isupper int isupper(int ch) 检查ch是否是大写字母(A-Z) 是返回非0,否则返回0
tolower int tolower(int ch) 将ch字符转换为小写字母 返回ch所代表的字符的小写字母
toupper int toupper(int ch) 将ch字符转换成大写字母 与ch相应的大写字母
isalnum int isalnum(int ch) 检查ch是否是字母或数字 是字母或数字返回非0,否则返回0
isprint int isprint(int ch) 检查ch是否是可打印字符(包括空格),其ASCII码在0x20到0x7E之间 是返回非0,否则返回0
ispunct int ispunct(int ch) 检查ch是否是标点字符(不包括空格),即除字母,数字和空格以外的所有可打印字符 是返回非0,否则返回0
isspace int isspace(int ch) 检查ch是否是空格符和跳格符(控制字符)或换行符 是返回非0,否则返回0
isxdigit int isxdigit(int ch) 检查ch是否是一个16进制数学字符(即0-9,或A-F,或a-f) 是返回非0,否则返回0
isascii int isascii(int ch) 测试参数是否是ASCII码0-127 是返回非0,否则返回0

7、素数系列

  • 埃拉托斯特尼筛法,或者叫埃氏筛法
const int N = 100005;  
bool prime[N];  
void init(){  
    for(int i=2;itrue;//先全部初始化为素数  
    for(int i=2;i*iif(prime[i]){//如果i是质数  
            for(int j=i*i;j//从i的两倍开始的所有的倍数(i*i也行)  
                prime[j] = false;  
            }  
        }  
    }  
}  
  • 预处理每个数的所有质因数
const int N = 100000 + 5;  
vector<int > prime_factor[N];  
void init(){  
    for(int i = 2; i < N; i ++){  
        if(prime_factor[i].size() == 0){//如果i是质数   
            for(int j = i; j < N; j += i){  
                prime_factor[j].push_back(i);   
            }  
        }  
    }  
} 
  • 比如预处理每个数的所有因数
const int N = 100000 + 5;  
vector<int > factor[N];  
void init(){  
    for(int i = 2; i < N; i ++){  
        for(int j = i; j < N; j += i){  
            factor[j].push_back(i);  
        }  
    }  
}
  • 预处理每个数的质因数分解
const int N = 100000 + 5;  
vector<int > prime_factor[N];  
void init(){  
    int temp;  
    for(int i = 2; i < N; i ++){  
        if(prime_factor[i].size() == 0){  
            for(int j = i; j < N; j += i){  
                temp = j;  
                while(temp % i == 0){  
                    prime_factor[j].push_back(i);  
                    temp /= i;  
                }  
            }  
        }  
    }  
}

http://blog.csdn.net/laichilizi/article/details/79390020

质因数分解题目↑(输入两个整数a、b,每行输出一个数的分解,形如k=a1*a2*a3…(a1<=a2<=a3….)

#include    
using namespace std;    
const int N = 100000 + 5;    
vector<int > prime_factor[N];    
void init(){    
    int temp;    
    for(int i = 2; i < N; i ++){    
        if(prime_factor[i].size() == 0){    
            for(int j = i; j < N; j += i){    
                temp = j;    
                while(temp % i == 0){    
                    prime_factor[j].push_back(i);    
                    temp /= i;    
                }    
            }    
        }    
    }    
}    
int main()  
{    
    init();   

    int a,b;  
    scanf("%d %d",&a,&b);  
    vector<int>::iterator it;  
    for(int i=a;i<=b;i++){  
        cout<"=";  
        for(it = prime_factor[i].begin(); it != prime_factor[i].end(); it++){  
            if(it != prime_factor[i].end()-1) cout << *it <<"*";  
            else cout<<*it<

8、BFS系列

http://blog.csdn.net/laichilizi/article/details/75024841

  • 马的移动↑,BFS入门题

http://blog.csdn.net/laichilizi/article/details/79383954

  • 学霸的迷宫↑,BFS+记录操作方向

9、DFS系列

http://blog.csdn.net/laichilizi/article/details/79388913

  • 正则问题↑,一个由x()|组成的正则表达式。输入长度不超过100,保证合法,求这个正则表达式能接受的最长字符串的长度

http://blog.csdn.net/laichilizi/article/details/78714582

  • POJ-2386,简单区域连通问题,↑

10、动态规划(01背包,完全背包)

  • 01背包(记忆化搜索)
#include  
using namespace std;  
typedef long long ll;  

int n,W;  
int dp[105][10005];  
int w[105],v[105];  

int rec(int i,int j){  
    if(dp[i][j] >= 0){  
        return dp[i][j];  
    }  
    int res;  
    if(i == n){  
        res = 0;  
    }else if(j < w[i]){  
        res = rec(i + 1,j);  
    }else{  
        res = max(rec(i + 1,j),rec(i + 1,j - w[i])+v[i]);  
    }  

    return dp[i][j] = res;  
}  

int main()  
{  
    scanf("%d %d",&n,&W);  

    for(int i=0;iscanf("%d %d",&w[i],&v[i]);   
    }  

    memset(dp,-1,sizeof(dp));  

    cout<0,W)<return 0;  
}  
  • 01背包(二维DP)
#include  
using namespace std;  
typedef long long ll;  

int n,W;  
int dp[105][10005];  
int w[105],v[105];  

int main()  
{  
    scanf("%d %d",&n,&W);  

    for(int i=0;iscanf("%d %d",&w[i],&v[i]);   
    }  

    memset(dp,0,sizeof(dp));  

    for(int i=n-1;i>=0;i--){  
        for(int j=0;j<=W;j++){  
            if(j < w[i]){  
                dp[i][j] = dp[i + 1][j];  
            }else{  
                dp[i][j] = max(dp[i+1][j],dp[i+1][j-w[i]]+v[i]);  
            }  
        }  
    }  

    cout<0][W]<return 0;  
}  
  • 01背包(一维最简化)
int dp[MAX_W +1];

void solve(){
    for(int i=0;ifor(int j=W;j>=w[i];j--){
            dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
        }
    }
    printf("%d\n",dp[W]);
}
  • 完全背包(一维最简化)
int dp[MAX_W +1];

void solve(){
    for(int i=0;ifor(int j=w[i];j<=W;j--){
            dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
        }
    }
    printf("%d\n",dp[W]);
}

11、并查集

int par[MAX_N];
int rank[MAX_N];

//初始化n个元素 
void init(int n){
    for(int i=0;i0;
    }
}

//查询树的根 
int find(int x){
    if(par[x] == x){
        return x;
    }else{
        return par[x] = find(par[x]);
    }
}

//合并x和y所属的集合 
void unite(int x,int y){
    x = find(x);
    y = find(y);
    if(x == y) return;
    if(rank[x] < rank[y]){
        par[x] = y;
    }else{
        par[y] = x;
        if(rank[x] == rank[y]) rank[x]++;
    }
}

//判断x和y是否属于同一个集合
bool same(int x,int y){
    return find(x) == find(y);
}

经典例题:hdu1232

#include
using namespace std;

int par[1005];
int rank[1005];

//初始化n个元素
void init(int n){
    for(int i=1;i<=n;i++){
        par[i] = i;
        rank[i] = 0;
    }
}

//查询树的根
int find(int x){
    if(par[x] == x){
        return x;
    }else{
        return par[x] = find(par[x]);
    }
}

//合并x和y所属的集合
void unite(int x,int y){
    x = find(x);
    y = find(y);
    if(x == y) return;
    if(rank[x] < rank[y]){
        par[x] = y;
    }else{
        par[y] = x;
        if(rank[x] == rank[y]) rank[x]++;
    }
}

//判断x和y是否属于同一个集合
bool same(int x,int y){
    return find(x) == find(y);
}

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        if(n == 0) break;
        init(n);
        for(int i=0;iint x,y;
            scanf("%d%d",&x,&y);
            unite(x,y);
        }
        int ans = 0;
        for(int i=1;i<=n;i++){
            if(par[i] == i) ans ++;
        }
        printf("%d\n",ans-1);
    }

    return 0;
}

12、树状数组

int lowbit(int x){  
    return x&(-x);  
}  

void add(int x,int y){  
    while(x<=n){  
        c[x]+=y;  
        x+=lowbit(x);  
    }  
}  

//用于求序列中小于等于数字x的元素的个数
int sum(int x){  
    int ans=0;  
    while(x>0){  
        ans+=c[x];  
        x-=lowbit(x);  
    }       
    return ans;  
} 

http://blog.csdn.net/laichilizi/article/details/79450529

  • 求逆序数↑
#include  
#include  
using namespace std;  
int n;  
int aa[100005];  
int c[100005];  
int a[100005];  
int lowbit(int x)  
{  
    return x&(-x);  
}  
void add(int x,int y)  
{  
    while(x<=n)  
    {  
        c[x]+=y;  
        x+=lowbit(x);  
    }  
}  
int sum(int x)  
{  
    int ans=0;  
    while(x>0)  
    {  
        ans+=c[x];  
        x-=lowbit(x);  
    }  

    return ans;  
}  

int main()  
{  
    scanf("%d",&n);  
    for(int i=0;iscanf("%d",&a[i]);  
    //  aa[a[i]]=a[i];  
    }   long long aans=0;  
    for(int i = 0 ; i1);  
        aans+=i-sum(a[i]-1);  
    //  cout << i;  
    //  cout << sum(a[i]-1)<
    }  

    printf("%lld\n",aans);  
    return 0;  
} 

13、快速幂 and 矩阵快速幂

  • 快速幂
typedef long long ll;
ll ksm(ll x,ll n,ll mod){
    ll res = 1;
    x = x % mod;
    while(x > 0){
        if(n & 1) res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}
  • 矩阵快速幂

http://blog.csdn.net/wust_zzwh/article/details/52058209

#include  
using namespace std;  
typedef long long ll;  
const int MOD=10000;  
struct mat  
{  
    ll a[2][2];  
};  
mat mat_mul(mat x,mat y)  
{  
    mat res;  
    memset(res.a,0,sizeof(res.a));  
    for(int i=0;i<2;i++)  
        for(int j=0;j<2;j++)  
        for(int k=0;k<2;k++)  
        res.a[i][j]=(res.a[i][j]+x.a[i][k]*y.a[k][j])%MOD;  
    return res;  
}  
void mat_pow(int n)  
{  
    mat c,res;  
    c.a[0][0]=c.a[0][1]=c.a[1][0]=1;  
    c.a[1][1]=0;  
    memset(res.a,0,sizeof(res.a));  
    for(int i=0;i<2;i++) res.a[i][i]=1;  
    while(n)  
    {  
        if(n&1) res=mat_mul(res,c);  
        c=mat_mul(c,c);  
        n=n>>1;  
    }  
    printf("%I64d\n",res.a[0][1]);  
}  
int main()  
{  
    int n;  
    while(~scanf("%d",&n)&&n!=-1)  
    {  
        mat_pow(n);  
    }  
    return 0;  
}

14、最短路

http://blog.csdn.net/acm_1361677193/article/details/48211319

15、string

http://blog.csdn.net/tengfei461807914/article/details/52203202

16、二分搜索

二分模型

while(l < r-1){
        int mid = r + (l - r)>> 1;
        if() l = mid;
        else r = mid;
    }

http://blog.csdn.net/laichilizi/article/details/79388555

  • 分巧克力↑,经典二分题

17、字符串函数

  • 字符处理函数:ctype.h
int isdigit(int ch)  ;//是否为数字,即ch是否是0-9中的字符
int isxdigit(int ch) ;//是否为十六进制数字,即ch是否是0-9  a-z A-Z 中的字符
int isalpha(int ch)  ;//是否为字母
int isalnum(int ch)  ;//是否为字母或数字
int islower(int ch)  ;//是否为小写字母
int isupper(int ch)  ;//是否为大写字母
int tolower(int ch)  ;//转换为小写字母
int toupper(int ch)  ;//转换为大写字母
  • 字符串转换函数:stdlib.h

字符转换为数字:

double atof(char  *str) ; //将字符串str转换为double型数字
int    atoi (char  *str) ; //将字符串str转换为int 型数字
long   atol(char  *str) ; //将字符串str转换为long int 型数字

数字转换为字符:

//将int型数字digit按radix进制转换成字符串destStr
char * itoa (int digit, char *destStr, int radix) ;
//同理将long型数字转换成字符串
char * ltoa (long digit, char *destStr, int radix) ;
//同理将unsignedlong型数字转换成字符串
char * ultoa (long digit, char *destStr,int radix) ;

【以上库函数能够用于进制的转换】

类似函数还有:

double strtod(char *, char **) ;
long strtol(char *, char **, int) ;
unsigned long strtoul(char *, char **, int) ;
  • 字符串操作函数:string.h
char * strcpy (char *s1, char *s2) ; //将字符串s2拷贝到数组s1中。
char * strncpy(char *s1,char *s2) ; //将字符串s2的最多n个字符拷贝到数组s1中
char * strcat (char *s1, char * s2) ; //将字符串s2连接在字符串s1尾部
char * strncat(char *s1, char *s2, size_tn) ; //将字符串s2中最多n个字符连接在s1之后

【注意:以上操作都要求目标字符数组有足够的存储空间】

  • 符串比較函数:string.h
//比較字符串s1,s2.假设s1等于小于或大于s2。分别返回0。负值,正值
int strcmp(char *s1, char *s2 ) ;
int stricmp(char *s1, char *s2) ;//不区分大写和小写地比較两字符串
int strncmp(char *s1, char *s2, size_t n)  ;//比較两字符串的至多n个字符
  • 字符串查找函数:string.h
//在字符串str中查找字符ch第一次出现的位置,假设找到了,就返回str中ch的指针,否则返回NULL
char *strchr(char*str, int ch) ;
//查找字符串str中字符ch的最后一次出现的位置(即:从后往前查找)
char*strrchr(char *str, int ch) ;
//查找字符串str1中第一次出现字符串str2的位置
char *strstr(char*str1, char *str2) ;
//查找字符串str2中随意字符在字符串str1中首次出现的位置。
char*strpbrk(char *str1, char *str2)

其他函数:

char *strrev(char * ) ; //字符串逆序函数
size_t strlen(char * str) ;//測字符串str的长度

注意:

strncpy( ) , strncat( ) , strncmp( ) ,这些函数仅仅能对两个不同的字符串操作,不能对同一字符串的不同部分操作。假设须要这么做,能够使用内存函数。

若把目标字符串初始置空,strncat()能够完毕非常多功能的操作。能够替代strncpy( )的功能,还能够提取子串等。

18、set & multiset

19、map & multimap

20、queue & priority_queue

  • queue

queue入队,如例:q.push(x); 将x 接到队列的末端。

queue出队,如例:q.pop(); 弹出队列的第一个元素,注意,并不会返回被弹出元素的值。

访问queue队首元素,如例:q.front(),即最早被压入队列的元素。

访问queue队尾元素,如例:q.back(),即最后被压入队列的元素。

判断queue队列空,如例:q.empty(),当队列空时,返回true。

访问队列中的元素个数,如例:q.size()

  • priority_queue

empty() 如果优先队列为空,则返回真

pop() 删除第一个元素

push() 加入一个元素

size() 返回优先队列中拥有的元素的个数

top() 返回优先队列中有最高优先级的元素

21、stack

empty() 堆栈为空则返回真

pop() 移除栈顶元素

push() 在栈顶增加元素

size() 返回栈中元素数目

top() 返回栈顶元素

22、vector

http://blog.csdn.net/duan19920101/article/details/50617190/

23、贪心算法

http://blog.csdn.net/qq_32400847/article/details/51336300

24、数制转换

http://blog.csdn.net/laichilizi/article/details/79381732

25、LCS

只计算LCS的数目

#include
using namespace std;
typedef long long ll;

char a[1005],b[1005],ans[1005];
int dp[1005][1005];
int main()
{
    //freopen("input.txt","r",stdin);

    scanf("%s %s",a+1,b+1);
    int n = strlen(a+1);
    int m = strlen(b+1);
    memset(dp,0,sizeof(dp));

    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(a[i] == b[j]){
                dp[i][j] = dp[i-1][j-1] + 1;
            }else{
                dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
            }
        }
    }

    cout<return 0;
}

除了LCS的数目,还输出对应的子串

#include
using namespace std;
typedef long long ll;

char a[1005],b[1005],ans[1005];
int dp[1005][1005];
int main()
{
    //freopen("input.txt","r",stdin);

    scanf("%s %s",a+1,b+1);
    int n = strlen(a+1);
    int m = strlen(b+1);
    memset(dp,0,sizeof(dp));

    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(a[i] == b[j]){
                dp[i][j] = dp[i-1][j-1] + 1;
            }else{
                dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
            }
        }
    }
    int cur = 0;
    for(int i=n,j=m;dp[i][j];--i,--j){
        while(dp[i][j] == dp[i-1][j]) --i;
        while(dp[i][j] == dp[i][j-1]) --j;
        ans[cur++] = a[i];
    }

    reverse(ans,ans+cur);
    //ans[cur] = '\0';
    printf("%s\n",ans);

    return 0;
}

26、LIS

#include  
using namespace std;  
#define INF 0x3f3f3f3f  
typedef long long ll;  

int dp[105];  
int a[105];  

int main()  
{  
    int n;  
    scanf("%d",&n);  
    for(int i=0;iscanf("%d",&a[i]);  

    int res = 0;  
    for(int i=0;i1;  
        for(int j=0;jif(a[j] < a[i]){ //如果是包括相等,就改为<=   
                dp[i] = max(dp[i],dp[j]+1);  
            }  
        }  
        res = max(res,dp[i]);   
    }  
    cout<return 0;  
}

27、最大字段和

#include
using namespace std;
typedef long long ll;

ll dp[50005];

ll solve(int n,ll *dp){
    ll ans = 0,b = 0;
    for(int i=0;iif(b>=0) b+=dp[i];
        else b = dp[i];
        if(b>ans) ans = b;
    }
    return ans;
}

int main(){
    //freopen("input.txt","r",stdin);
    int n;
    scanf("%d",&n);
    memset(dp,0,sizeof(dp));

    for(int i=0;iscanf("%lld",&dp[i]);
    ll p = solve(n,dp);
    cout<return 0;
}

28、循环数组最大字段和

#include
using namespace std;
typedef long long ll;

ll dp[50005];

ll solve(int n,ll *dp){
    ll ans = 0,b = 0;
    for(int i=0;iif(b>=0) b+=dp[i];
        else b = dp[i];
        if(b>ans) ans = b;
    }
    return ans;
}

int main(){
    freopen("input.txt","r",stdin);
    int n,sum = 0;
    scanf("%d",&n);
    memset(dp,0,sizeof(dp));

    for(int i=0;iscanf("%lld",&dp[i]);
        sum += dp[i];
    }
    ll ans1 = solve(n,dp);

    for(int i=0;icout<return 0;
}

29、最小正字段和

#include  
#include  
#include  
#include  
using namespace std;  
typedef long long LL;  
#define INF 0x3f3f3f3f;

struct node{  
    LL a;  
    int id_mi;  
    int id_ma;  
};  
bool operator <(node a,node b){  
    return a.aa;  
}  
node dp[50010];  
int main(){  
    int n,a,i,j;  
    scanf("%d",&n);  
    LL ans=0;  
    dp[0].a=0;  
    dp[0].id_mi=0;  
    dp[0].id_ma=0;  
    for(i=1;i<=n;i++){  
        scanf("%lld",&dp[i].a);  
        dp[i].a+=dp[i-1].a;  
        dp[i].id_mi=i;  
        dp[i].id_ma=i;  

    }  
    sort(dp,dp+1+n);  
    j=0;  
    for(i=1;i<=n;i++)  
    {  
        if(dp[i].a==dp[j].a){  
            dp[j].id_mi=max(dp[j].id_mi,dp[i].id_mi);  
            dp[j].id_ma=min(dp[j].id_ma,dp[i].id_ma);  
        }  
        else dp[++j]=dp[i];  
    }  
    for(i=0;iif(dp[i].id_mi1].id_ma&&dp[i+1].a-dp[i].a>0&&(ans==0||ans>dp[i+1].a-dp[i].a)){  
            ans=dp[i+1].a-dp[i].a;  
        }  
    }  
    cout<return 0;  
}  

30、大数运算

http://blog.csdn.net/tt2767/article/details/45420067

31、Trie

const int MAX_N = 10000;  // Trie 树上的最大结点数
const int MAX_C = 26;  // 每个结点的子结点个数上限
struct Trie {
    int *ch[MAX_N];  // ch 保存了每个结点的 26 个可能的子结点编号,26 对应着 26 种小写字母,也就是说,插入的字符串全部由小写字母组成。初始全部为 -1
    int tot;  // 总结点个数(不含根结点),初始为 0
    int cnt[MAX_N];  // 以当前结点为终端结点的 Trie 树中的字符串个数

    void init() {  // 初始化 Trie 树,根结点编号始终为 0
        tot = 0;
        memset(cnt, 0, sizeof(cnt));
        memset(ch, 0, sizeof(ch));  // 将 ch 中的元素初始化为 NULL
    }

    void insert(char *str) {
        int p = 0;  // 从根结点(0)出发
        for (int i = 0; str[i]; ++i) {
            if (ch[p] == NULL) {
                ch[p] = new int[MAX_C];  // 只有 p 当包含子结点的时候才去开辟 ch[p] 的空间
                memset(ch[p], -1, sizeof(int) * MAX_C);  // 初始化为 -1
            }
            if (ch[p][str[i] - 'a'] == -1) {  // 该子结点不存在
                ch[p][str[i] - 'a'] = ++tot;  // 新增结点
            } 
            p = ch[p][str[i] - 'a'];  // 在 Trie 树上继续插入字符串 str
        }
        cnt[p]++;
    }

    int find(char *str) {  // 返回字符串 str 的出现次数
        int p = 0;
        for (int i = 0; str[i]; ++i) {
            if (ch[p][str[i] - 'a'] == -1) {
                return 0;
            }
            p = ch[p][str[i] - 'a'];
        }
        return cnt[p];
    }
};

你可能感兴趣的:(acm)