NOIP2014八校联考第3场第1试10.4】反抗希碧拉系统续(regex)

Description

虽然一科的反抗行动失败了,但那次行动已使反抗希碧拉系统的观念深入人心,而作为分析官的你也找到了系统的某处漏洞,机会依然存在,你要为下一次反抗做好准备。
直接使用电磁脉冲破坏系统在上次被证明是不可行的,现在只能试图渗透进系统寻找突破口。你现在可以从漏洞监听到中枢中每个单元大脑间的通信,并筛选出其中符合某规则的一些进行更深入的分析。规则可以描述为一个特殊的正则表达式,有如下递归定义:
元素:=“[”+字符集+“]”,表示匹配字符集中的任意一个字符。字符集中的字符都是小写英文字母。“+”表示字符串的连接。
表达式:=元素 或 表达式+表达式 或 “(”+表达式+“)”+“+”。相连的表达式表示匹配连续的字符。“+”表示前面括号中的内容出现一次或多次。
例如:表达式[a][b]匹配ab;表达式[ab][c]匹配ac或bc;表达式([a][b])+([c])+可以匹配“abc”、“ababc”、“ababccc”等串。
你的同事决定使用巨型计算机穷举每个符合要求的定长字符串(通信)并分析其性质。你对这种方法持怀疑态度,较长的运算时间以及可能比中枢还大的耗电量必然会引起系统的怀疑。你需要计算有多少满足要求的字符串以估算计算所需时间,你坚信分析正则表达式作为一项分析官必备的技能并不会太困难。

Solution

这道题又长又烦,看了就不想打。
但是看了题解之后,发现非常的机智。
方法是构造一个自动机。
首先是’[ ]’,这个是一个元素,开两个点,一个s和一个t,然后前面的元素的t连向后面元素的s(连一条无色的边),然后s向t连很多条中括号里面的颜色的边。
那么对于’( )’,把最后的那个颜色的t连向最前面的元素的s,这样就可以实现循环了。
然后设f[i][j]表示做到第i个字符,走到第j个团(自己的元素没有向外的连边),那么可以处理一个a[i][j]表示从第i团转移到第j团的方案数。
那么直接用矩阵乘法优化就好了。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef unsigned int ui;
typedef long long ll;
const int maxn=1007;
int i,j,k,l,t,n,num,r,tot;
int a[11][11][27],c[10000],er[21],b[maxn*10];
ui ans,f[maxn][maxn],x[maxn][maxn],p[maxn][maxn],q[maxn][maxn],z[maxn][maxn];
ll mo;
int m;
char s[maxn];
void qsm(int xx)
{
    int i,j,k;
    if(xx==1)return;
    qsm(xx/2);
    fo(i,1,tot)fo(j,1,tot){
        z[i][j]=0;
        fo(k,1,tot)z[i][j]+=x[i][k]*x[k][j];
    }
    fo(i,1,tot)fo(j,1,tot)x[i][j]=z[i][j];
    if(xx%2){
        fo(i,1,tot)fo(j,1,tot){
            z[i][j]=0;
            fo(k,1,tot)z[i][j]+=x[i][k]*p[k][j];
        }
        fo(i,1,tot)fo(j,1,tot)x[i][j]=z[i][j];
    }
}
void dfs(int x,int &y){
    int i;
    y|=er[x-1];
    fo(i,1,num)if(a[x][i][26])dfs(i,y);
}
int main(){
//  freopen("fan.in","r",stdin);
    er[0]=1;fo(i,1,20)er[i]=er[i-1]*2;
    mo=4294967296;
    scanf("%s",s+1);n=strlen(s+1);
    scanf("%d",&m);
    fo(i,1,n){
        if(s[i]=='['){
            if(num)a[num][num+1][26]=1;
            fo(j,i+1,n){

                if(s[j]==']'){c[j]=num/2+1;break;}
                a[num+1][num+2][s[j]-'a']=1;
            }
            num+=2;
        }
    }
    fo(i,1,n){
        if(s[i]!='+')continue;
        r=-1;l=100;k=0;
        fod(j,i-1,1){
            if(c[j])l=min(l,c[j]),r=max(r,c[j]);
            if(s[j]=='(')k++;if(s[j]==')')k--;
            if(!k)break;
        }
        a[2*r][2*l-1][26]=1;
    }
    fo(i,1,num)dfs(i,b[i]);
    memset(c,0,sizeof(c));
    fo(i,0,er[num]-1){
        k=0;
        fo(j,1,num)if(er[j-1]&i)k|=b[j];
        if(i==k)c[i]=++tot;
    }
    fo(i,0,25){
        fo(j,0,er[num]-1){
            if(!c[j])continue;t=0;
            fo(k,1,num){
                if(!(er[k-1]&j))continue;
                fo(l,1,num){
                    if(a[k][l][i])t|=b[l];
                }
            }
            f[c[j]][c[t]]++;
        }
    }
    fo(i,1,tot)fo(j,1,tot)x[i][j]=p[i][j]=f[i][j];
    qsm(m);
    fo(i,0,er[num]-1)if((i&er[num-1])&&c[i])ans=(ans+z[c[b[1]]][c[i]])%mo;
    printf("%lld\n",ans);
}

你可能感兴趣的:(noip,字符串,DP,矩阵乘法)