您的位置:首页 > 其它

AC自动机专题——E - Censored! URAL - 1158 大数+DP+AC自动机

2017-04-16 13:20 393 查看
The alphabet of Freeland consists of exactly N letters. Each sentence of Freeland language (also known as Freish) consists of exactly M letters without word breaks. So, there exist exactly N M different Freish sentences.

But after recent election of Mr. Grass Jr. as Freeland president some words offending him were declared unprintable and all sentences containing at least one of them were forbidden. The sentence S contains a word W if W is a
substring of S i.e. exists such k >= 1 that S[k] = W[1], S[k+1] = W[2], ... , S[k+len(W)-1] = W[len(W)], where k+len(W)-1 <= M and len(W) denotes length of W. Everyone who uses a forbidden sentence is to be put to jail for 10 years.

Find out how many different sentences can be used now by freelanders without risk to be put to jail for using it.

Input

The first line contains three integer numbers: N - the number of letters in Freish alphabet, M - the length of all Freish sentences and P - the number of forbidden words (1 ≤ N ≤ 50, 1 ≤ M ≤ 50, 0 ≤ P ≤ 10).

The second line contains exactly N different characters - the letters of the Freish alphabet (all with ASCII code greater than 32).

The following P lines contain forbidden words, each not longer than min(M, 10) characters, all containing only letters of Freish alphabet.

Output

Output the only integer number - the number of different sentences freelanders can safely use.

Example
inputoutput
3 3 3
QWE
QQ
WEE
Q

题目大意:求给定长度不出现给定单词的字符串数量

解题思路:利用DP进行求解,dp[i][j]代表长度为i的字符串在j个点的情况下,其字符串的数量,

其DP思路可以参考Floy算法,一维为规模,第二个为点数。

StatusAccepted
Time156ms
Memory45984kB
Length11807
LangG++ 4.9 C++11
Submitted2017-04-12 12:28:23
Shared
RemoteRunId7349366
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iomanip>
#include <algorithm>
#include <climits>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#include <list>
#define rep(i,m,n) for(int i=m;i<=n;i++)
#define rsp(it,s) for(set<int>::iterator it=s.begin();it!=s.end();it++)
const int inf_int = 2e9;
const long long inf_ll = 2e18;
#define inf_add 0x3f3f3f3f
#define MOD 1000000007
#define pb push_back
//#define mp make_pair
#define fi first
#define se second
#define pi acos(-1.0)
#define pii pair<int,int>
#define Lson L, mid, rt<<1
#define Rson mid+1, R, rt<<1|1
const int maxn=5e2+10;
using namespace std;
typedef vector<int> vi;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){int ra,fh;char rx;rx=getchar(),ra=0,fh=1;
while((rx<'0'||rx>'9')&&rx!='-')rx=getchar();if(rx=='-')
fh=-1,rx=getchar();while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,
rx=getchar();return ra*fh;}
//#pragma comment(linker, "/STACK:102400000,102400000")
ll gcd(ll p,ll q){return q==0?p:gcd(q,p%q);}
ll qpow(ll p,ll q){ll f=1;while(q){if(q&1)f=f*p;p=p*p;q>>=1;}return f;}

int dir[8][2]={{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1}};

map<char, int> mp;

#define MAX_L 205 //最大长度,可以修改
class bign
{
public:
int len, s[MAX_L];//数的长度,记录数组
//构造函数
bign();
bign(const char*);
bign(int);
bool sign;//符号 1正数 0负数
string toStr() const;//转化为字符串,主要是便于输出
friend istream& operator>>(istream &,bign &);//重载输入流
friend ostream& operator<<(ostream &,bign &);//重载输出流
//重载复制
bign operator=(const char*);
bign operator=(int);
bign operator=(const string);
//重载各种比较
bool operator>(const bign &) const;
bool operator>=(const bign &) const;
bool operator<(const bign &) const;
bool operator<=(const bign &) const;
bool operator==(const bign &) const;
bool operator!=(const bign &) const;
//重载四则运算
bign operator+(const bign &) const;
bign operator++();
bign operator++(int);
bign operator+=(const bign&);
bign operator-(const bign &) const;
bign operator--();
bign operator--(int);
bign operator-=(const bign&);
bign operator*(const bign &)const;
bign operator*(const int num)const;
bign operator*=(const bign&);
bign operator/(const bign&)const;
bign operator/=(const bign&);
//四则运算的衍生运算
bign operator%(const bign&)const;//取模(余数)
bign factorial()const;//阶乘
bign Sqrt()const;//整数开根(向下取整)
bign pow(const bign&)const;//次方
//一些乱乱的函数
void clean();
~bign();
};
#define max(a,b) a>b ? a : b
#define min(a,b) a<b ? a : b

bign::bign()
{
memset(s, 0, sizeof(s));
len = 1;
sign = 1;
}

bign::bign(const char *num)
{
*this = num;
}

bign::bign(int num)
{
*this = num;
}

string bign::toStr() const
{
string res;
res = "";
for (int i = 0; i < len; i++)
res = (char)(s[i] + '0') + res;
if (res == "")
res = "0";
if (!sign&&res != "0")
res = "-" + res;
return res;
}

istream &operator>>(istream &in, bign &num)
{
string str;
in>>str;
num=str;
return in;
}

ostream &operator<<(ostream &out, bign &num)
{
out<<num.toStr();
return out;
}

bign bign::operator=(const char *num)
{
memset(s, 0, sizeof(s));
char a[MAX_L] = "";
if (num[0] != '-')
strcpy(a, num);
else
for (int i = 1; i < strlen(num); i++)
a[i - 1] = num[i];
sign = !(num[0] == '-');
len = strlen(a);
for (int i = 0; i < strlen(a); i++)
s[i] = a[len - i - 1] - 48;
return *this;
}

bign bign::operator=(int num)
{
char temp[MAX_L];
sprintf(temp, "%d", num);
*this = temp;
return *this;
}

bign bign::operator=(const string num)
{
const char *tmp;
tmp = num.c_str();
*this = tmp;
return *this;
}

bool bign::operator<(const bign &num) const
{
if (sign^num.sign)
return num.sign;
if (len != num.len)
return len < num.len;
for (int i = len - 1; i >= 0; i--)
if (s[i] != num.s[i])
return sign ? (s[i] < num.s[i]) : (!(s[i] < num.s[i]));
return !sign;
}

bool bign::operator>(const bign&num)const
{
return num < *this;
}

bool bign::operator<=(const bign&num)const
{
return !(*this>num);
}

bool bign::operator>=(const bign&num)const
{
return !(*this<num);
}

bool bign::operator!=(const bign&num)const
{
return *this > num || *this < num;
}

bool bign::operator==(const bign&num)const
{
return !(num != *this);
}

bign bign::operator+(const bign &num) const
{
if (sign^num.sign)
{
bign tmp = sign ? num : *this;
tmp.sign = 1;
return sign ? *this - tmp : num - tmp;
}
bign result;
result.len = 0;
int temp = 0;
for (int i = 0; temp || i < (max(len, num.len)); i++)
{
int t = s[i] + num.s[i] + temp;
result.s[result.len++] = t % 10;
temp = t / 10;
}
result.sign = sign;
return result;
}

bign bign::operator++()
{
*this = *this + 1;
return *this;
}

bign bign::operator++(int)
{
bign old = *this;
++(*this);
return old;
}

bign bign::operator+=(const bign &num)
{
*this = *this + num;
return *this;
}

bign bign::operator-(const bign &num) const
{
bign b=num,a=*this;
if (!num.sign && !sign)
{
b.sign=1;
a.sign=1;
return b-a;
}
if (!b.sign)
{
b.sign=1;
return a+b;
}
if (!a.sign)
{
a.sign=1;
b=bign(0)-(a+b);
return b;
}
if (a<b)
{
bign c=(b-a);
c.sign=false;
return c;
}
bign result;
result.len = 0;
for (int i = 0, g = 0; i < a.len; i++)
{
int x = a.s[i] - g;
if (i < b.len) x -= b.s[i];
if (x >= 0) g = 0;
else
{
g = 1;
x += 10;
}
result.s[result.len++] = x;
}
result.clean();
return result;
}

bign bign::operator * (const bign &num)const
{
bign result;
result.len = len + num.len;

for (int i = 0; i < len; i++)
for (int j = 0; j < num.len; j++)
result.s[i + j] += s[i] * num.s[j];

for (int i = 0; i < result.len; i++)
{
result.s[i + 1] += result.s[i] / 10;
result.s[i] %= 10;
}
result.clean();
result.sign = !(sign^num.sign);
return result;
}

bign bign::operator*(const int num)const
{
bign x = num;
bign z = *this;
return x*z;
}
bign bign::operator*=(const bign&num)
{
*this = *this * num;
return *this;
}

bign bign::operator /(const bign&num)const
{
bign ans;
ans.len = len - num.len + 1;
if (ans.len < 0)
{
ans.len = 1;
return ans;
}

bign divisor = *this, divid = num;
divisor.sign = divid.sign = 1;
int k = ans.len - 1;
int j = len - 1;
while (k >= 0)
{
while (divisor.s[j] == 0) j--;
if (k > j) k = j;
char z[MAX_L];
memset(z, 0, sizeof(z));
for (int i = j; i >= k; i--)
z[j - i] = divisor.s[i] + '0';
bign dividend = z;
if (dividend < divid) { k--; continue; }
int key = 0;
while (divid*key <= dividend) key++;
key--;
ans.s[k] = key;
bign temp = divid*key;
for (int i = 0; i < k; i++)
temp = temp * 10;
divisor = divisor - temp;
k--;
}
ans.clean();
ans.sign = !(sign^num.sign);
return ans;
}

bign bign::operator/=(const bign&num)
{
*this = *this / num;
return *this;
}

bign bign::operator%(const bign& num)const
{
bign a = *this, b = num;
a.sign = b.sign = 1;
bign result, temp = a / b*b;
result = a - temp;
result.sign = sign;
return result;
}

bign bign::pow(const bign& num)const
{
bign result = 1;
for (bign i = 0; i < num; i++)
result = result*(*this);
return result;
}

bign bign::factorial()const
{
bign result = 1;
for (bign i = 1; i <= *this; i++)
result *= i;
return result;
}

void bign::clean()
{
if (len == 0) len++;
while (len > 1 && s[len - 1] == '\0')
len--;
}

bign bign::Sqrt()const
{
if(*this<0)return -1;
if(*this<=1)return *this;
bign l=0,r=*this,mid;
while(r-l>1)
{
mid=(l+r)/2;
if(mid*mid>*this)
r=mid;
else
l=mid;
}
return l;
}

bign::~bign()
{
}

const int N = 555;
const int M = 101;

class Trie
{

public:
int next
[M],fail
,end
;
int root,L;
int newnode()
{
for(int i = 0;i < M;i++)//每一个节点对应0-128中的任意一个。
next[L][i] = -1;
end[L++] = -1;//表示下面没有节点 初始化,如果是记录次数,就赋0 还可以赋任意的数,
return L-1;
}
void init()
{
L = 0;
root = newnode();
}
void insert(char s[])
{
int len = strlen(s);
int now = root;
for(int i = 0;i < len;i++)
{
if(next[now][mp[s[i]]] == -1)
next[now][mp[s[i]]] = newnode();
now=next[now][mp[s[i]]];//记录其对应的节点编号
}
end[now]=1;//记录当前匹配单词的节点
//end[now]++;也可以用匹配单词结束后来记录次数
}
void build()
{
queue<int>Q;
fail[root] = root;//根节点仍然是根节点
for(int i = 0;i < M;i++)//对第一个字符遍历
if(next[root][i] == -1)//没有此字符开头
next[root][i] = root;//跳转到根
else//有此字符开头的
{
fail[next[root][i]] = root;//这个行位的失败指针为根
Q.push(next[root][i]);//行放入队列
}
while(!Q.empty())//还有字符
{
int now = Q.front();//逐层拿出第一个
Q.pop();
if(end[fail[now]]== 1) end[now] = 1;
for(int i = 0;i < M;i++)//对这一行
if(next[now][i] == -1)//如果下一行没有这个字符
next[now][i] = next[fail[now]][i];//他的下一个的这
else//如果有这个字符
{
fail[next[now][i]] = next[fail[now]][i];//他的下一个的
Q.push(next[now][i]);//下一行继续
}
}
}

/*bool used
;//判断其是否被查找到
bool query(char buf[],int n,int id)
{
int len = strlen(buf);
int now = root;
// memset(used,false,sizeof(used));//初始化used
bool flag = false;
for(int i = 0;i < len;i++)
{
now = next[now][buf[i]];
int temp = now;
while(temp != root)
{
if(end[temp] != -1)
{
//used[end[temp]] = true;//记录被匹配的信息
flag = true;
}
temp = fail[temp];
}
}

}*/
};

char buf[10005];

bign dp
[M];
int n,m,p;

Trie ac;

int main()
{
mp.clear();
scanf("%d %d %d",&n,&m,&p);
scanf("%s", buf);
for (int i = 0; i < n; i++) { mp[buf[i]] = i; }

ac.init();
while(p--)
{
scanf("%s", buf);
ac.insert(buf);
}

for (int i = 0; i < ac.L; i++)
{
for (int j = 0; j <= m; j++)
{
dp[i][j] = 0;
}
}

ac.build();

dp[0][0] = 1;
for (int i = 1; i <= m; i++)
{
for (int j = 0; j < ac.L; j++)
{
for (int k = 0; k < n; k++)
{
int tmp=ac.next[j][k];
if (ac.end[tmp]==-1)
{
dp[tmp][i] += dp[j][i - 1];
}
}
}
}
/* for (int i = 0; i < ac.L; i++)
{
for (int j = 0; j <= m; j++)
{
cout <<dp[i][j] <<" ";
}
cout<<endl;
}*/

bign ans1 = 0;

for (int i = 0; i < ac.L; i++) {
if(ac.end[i]==-1)
ans1 += dp[i][m];
}
cout << ans1 << endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: