POJ--1625[Censored!] AC自动机+DP+高精度

 

思路:

 

AC自动机+DP+高精度

 


细节错误:
(1):大数加法写错
(2):滚动数组更新写错


注意:
(1):若用矩阵乘法复杂度为O(snode^3*log(M)); TLE

(2):若用矩阵乘法的复杂度为O(snode*M*N);     AC

PS.函数传参时传引用会比传变量快

 

 

源代码:

/*AC代码:766ms*/
#include <iostream>
#include <cstdio>
#include <memory.h>
#include <algorithm>
#define INF 1e8
#define kind 50
#define MAXN 1005
#define max(a,b) (a>b?a:b)
using namespace std;
struct Node
{
 int next[kind],v,fail;
}Trie[20000];
int snode;//字典树的总结点数(总状态数)
int N,M,P;
int Q[10000],head,tail;
int hash[300];
int map[105][105];
char word[15];//模式串

struct Array
{
 int num[100],len;
 void Init()
 {
  len=0;
  memset(num,0,sizeof(num));
 }
}dp[2][105],ans,res;
/*------------------------AC自动机模版---------------------*/
void set_node(int x)
{
 memset(Trie[x].next,0,sizeof(Trie[x].next));
 Trie[x].v=0;//表示是否是危险结点
 Trie[x].fail=-1;
}
void Insert(char s[])
{
 int i=0,pos=0,index;
 while(s[i])
 {
  index=hash[s[i]];
  if(Trie[pos].next[index]==0)
  {
   set_node(++snode);
   Trie[pos].next[index]=snode;
  }
  if(Trie[pos].v)/*危险结点为其子结点不必继续(剪枝)*/
   break;
  pos=Trie[pos].next[index];
  i++;
 }
 Trie[pos].v++;
}
void Find_Fail(int pre,int ith,int now)
{
 if(!pre)//如果他的父节点是root,fail直接指向root
 {
   Trie[now].fail=0;
   return;
 }
 int temp=Trie[pre].fail;
 while(temp!=-1)
 {
  if(Trie[temp].next[ith]!=0)
  {
   Trie[now].fail=Trie[temp].next[ith];
   if(Trie[Trie[temp].next[ith]].v)//注意
    Trie[now].v++;
   return;
  }
  temp=Trie[temp].fail;
 }
 if(temp==-1)
  Trie[now].fail=0;
 return;
}
void Build_AC_Fail()
{
 int i,pos;
 head=tail=0;
 Trie[0].fail=-1;
 Q[head++]=0;
 while(head!=tail)
 {
  pos=Q[tail++];
  for(i=0;i<N;i++)
  {
   if(Trie[pos].next[i]!=0)//存在就要去找fail指针
   {
    Find_Fail(pos,i,Trie[pos].next[i]);
    Q[head++]=Trie[pos].next[i];
   }
   else//让它的next指向pos的fail的next
    Trie[pos].next[i]=Trie[Trie[pos].fail].next[i];
  }
 }
}
/*------------------------AC自动机模版---------------------*/
void Init()
{
 int i;
 char s[60];
 set_node(0);snode=0;
 scanf("%s",s);
 for(i=0;i<N;i++)
  hash[s[i]]=i;
 for(i=1;i<=P;i++)
 {
  scanf("%s",word);
  Insert(word);
 }
 /*
 for(i=0;i<N;i++)
  printf("%d ",hash[s[i]]);
 printf("\n");
 */
}
/*
void get_map()
{
 int i,j,u;
 memset(map,0,sizeof(map));
 for(i=0;i<=snode;i++)
 {
  if(Trie[i].v) continue;
  for(j=0;j<N;j++)
  {
   u=Trie[i].next[j];
   if(Trie[u].v)  continue;
   map[i][u]++;
  }
 }
 for(i=0;i<=snode;i++)
 {
  for(j=0;j<=snode;j++)
   printf("%d ",map[i][j]);
  printf("\n");
 }
}
*/
void Print(Array a)
{
 int i,len=a.len;
 for(i=len-1;i>=0;i--)
  printf("%d",a.num[i]);
 printf("\n");
}
void Add(Array &a,Array &b)
{
 int i,l;
 //if(a.len==0) a.len=1;
 //if(b.len==0) b.len=1;
 res.Init();
 l=max(a.len,b.len);
 //printf("*%d %d %d\n",a.len,b.len,l);
 res.len=l;
 for(i=0;i<l;i++)
 {
  res.num[i]+=(a.num[i]+b.num[i]);//注意+=
  res.num[i+1]+=res.num[i]/10;
  res.num[i]%=10;
 }
 if(res.num[l]!=0) res.len++;
 //Print(res);
}

void Solve()
{
 int i,j,k,temp;
 Build_AC_Fail();
 for(i=0;i<2;i++)
 {
  for(j=0;j<=snode;j++)
   dp[i][j].Init();
 }
 //get_map();
 dp[0][0].len=1;
 dp[0][0].num[0]=1;
 int id=0;
 for(i=0;i<=M-1;i++)
 {
  for(j=0;j<=snode;j++)
  {
   if(Trie[j].v) continue;
   if(dp[id][j].len==0) continue;
   for(k=0;k<N;k++)
   {
    temp=Trie[j].next[k];//化简
    if(!Trie[temp].v)
    {
     //printf("-----\n");
     //Print(dp[id][j]);
     //Print(dp[id^1][temp]);
     Add(dp[id^1][temp],dp[id][j]);
     dp[id^1][temp]=res;
     //Print(dp[id^1][temp]);
    }
   }
  }
  //注意滚动更新
  for(j=0;j<=snode;j++)
   dp[id][j].Init();
  id^=1;
 }
 ans.Init();
 ans.len=1;
 ans.num[0]=0;
 for(i=0;i<=snode;i++)
 {
  if(Trie[i].v) continue;
  Add(ans,dp[id][i]);
  ans=res;
  //Print(dp[id][i]);
 }
 Print(ans);
}
int main()
{
 scanf("%d%d%d",&N,&M,&P);
 Init();
 Solve();
return 0;
}


 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__简言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值