洛谷P1032 字串变换 【kmp,字符串hash】

版权声明:转载无所谓的。。。 https://blog.csdn.net/xuxiayang/article/details/82855959

大意

给定转换规则,求最小步数


思路

其实可以用AC自动机

这道题是问我们最小步数,因为其分支不大( 7 \leq7 )容易想到专门处理最优化问题的 b f s bfs 算法

b f s bfs 的匹配中,本人采用的是用字符数组模拟字符串中的运算,建立新的“ S t r i n g String ”,然后在匹配过程中,使用 K M P KMP 实现,对于判重,使用 h a s h + m a p hash+map


代码

#include<map>
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;char a[7][21],b[7][21];//转换规则串
const int base=233;//hash值
int n,ans,kmp[21];
unsigned long long zd;//目标串的hash值
map<unsigned long long,bool>m;//map库
struct String//定义一种结构体
{
    char c[101];//字符串
    int next[101],bs;//kmp_next数组,bs表示当前步数
    inline int size(){return strlen(c);}//长度
    inline void read(){scanf("%s",c);return;}//输入
    inline void build()//建立next数组
    {
        next[0]=-1;
        int j=-1;
        for(register int i=1;i<strlen(c);i++)
        {
            while(j>-1&&c[j+1]!=c[i]) j=next[j];
            if(c[j+1]==c[i]) j++;
            next[i]=j;
        }
        return;
    }
    inline int find(char s[])//查找s在c中出现的所有位置,并返回子串个数,复杂度n+m
    {
        int j=-1,len=0;
        for(register int i=0;i<strlen(c);i++)
        {
            while(j>-1&&s[j+1]!=c[i]) j=next[j];
            if(s[j+1]==c[i]) j++;
            if(j==strlen(s)-1) kmp[++len]=i-j,j=0;
        }
        return len;
    }
    inline unsigned long long hash()//自然溢出求hash值
    {
        unsigned long long ans=0;
        for(register int i=0;i<strlen(c);i++)ans=ans*base+c[i];
        return ans;
    }
}Begin,End;//定义起始串和目标串
inline int bfs()
{
    queue<String>q;//建立元素为结构体的队列,方便转移
    q.push(Begin);//放入
    while(q.size())
    {
        String x=q.front();x.build();q.pop();//取出并求出next数组
        if(x.bs==10) continue;//超出步数不管它
        for(register int i=0;i<n;i++)
        {
        	
            int len=x.find(a[i]),lena=strlen(a[i]),lenb=strlen(b[i]);
            if(!len) continue;//没有匹配串退出
            for(register int cs=1;cs<=len;cs++)
            {
            	String y=x;
            	y.bs=x.bs+1;//bs多了一步
            	int t=kmp[cs];//取出位置
            	if(lena==lenb)for(register int j=t;j<t+lena;j++) y.c[j]=b[i][j-t];//长度相同直接赋值
            	if(lena<lenb)//长度将变长
            	{
                    for(register int k=1;k<=lenb-lena;k++)
	                for(register int j=y.size()+(lenb-lena)-1;j>=t+lena;j--) 
	                y.c[j]=y.c[j-(lenb-lena)];//将后面的往前挪长度差次
	                for(register int j=t;j<t+lenb;j++) y.c[j]=b[i][j-t];//转换
	            }
	            if(lena>lenb)//长度将变短
	            {
	                for(register int j=t;j<t+lenb;j++) y.c[j]=b[i][j-t];//转换
	                for(register int k=1;k<=lena-lenb;k++)
	                for(register int j=t+lenb;j<y.size();j++) y.c[j]=y.c[j+1];//将前面的往后挪长度差位
	            }
	            unsigned long long hh=y.hash();//计算hash值
	            if(hh==zd) return y.bs;//到达终点,返回步数
	            if(m.find(hh)==m.end()) q.push(y),m[hh]=true;//若没有出现过,将其入队
	        }
	    }
    }
    return -1;
}
signed main()
{
    Begin.read();End.read();zd=End.hash();//输入
    while(scanf("%s %s",a[n],b[n])!=EOF)n++;//输入
    ans=bfs();//bfs
    if(ans<0) printf("NO ANSWER!");
    else printf("%d",ans);//输出
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/82855959