洛谷1032 字串变换

洛谷1032 字串变换

本题地址: http://www.luogu.org/problem/show?pid=1032

题目描述

  已知有两个字串 A$, B$ 及一组字串变换的规则(至多6个规则):
     A1$ -> B1$
     A2$ -> B2$
  规则的含义为:在 A$中的子串 A1$ 可以变换为 B1$、A2$ 可以变换为 B2$ …。
    例如:A$='abcd' B$='xyz'
  变换规则为:
    ‘abc’->‘xu’ ‘ud’->‘y’ ‘y’->‘yz’
  则此时,A$ 可以经过一系列的变换变为 B$,其变换的过程为:
   ‘abcd’->‘xud’->‘xy’->‘xyz’
  共进行了三次变换,使得 A$ 变换为B$。

输入输出格式

输入格式:

  键盘输人文件名。文件格式如下:
   A$ B$
   A1$ B1$ \
   A2$ B2$  |-> 变换规则
   ... ... / 
  所有字符串长度的上限为 20。

输出格式:

  输出至屏幕。格式如下:
  若在 10 步(包含 10步)以内能将 A$ 变换为 B$ ,则输出最少的变换步数;否则输出"NO ANSWER!"

输入输出样例

输入样例#1:
abcd xyz
abc xu
ud y
y yz
输出样例#1:
3

————————————————————————————————————————————

分析:最坏情况有20^10个点,大量不必要的情况被记录下来,但因为终点已知,双向BFS只要20^5。

 

//双向BFS。 
#include<stdio.h>
#include<string.h>
char A[100000][21]={{'\0'}},B[100000][21]={{'\0'}};
int main(void)
{
	char left[1001][21]={{'\0'}},right[1001][21]={{'\0'}};//记录变换规则 
	int i,j,lenc=0,lenl=0,lenr=0,step=0;
	scanf("%s%s",A[0],B[0]);//记录起点与终点,双向BFS 
	while(scanf("%s%s",left[lenl++],right[lenr++])==2) lenc++;//lenc:变换规则的数量 
	int h1=0,t1=1,h2=0,t2=1; //h为头,t为尾。 
	int delta=0,k=1,count1=0,count2=0;//k记录头结点个数,count记录头结点扩展出结点的数量
	char *ptr;//delta记录搜索到的字符串地址,ptr为函数返回值。 
	while(h1<t1 && h2<t2 && step<10)//每次循环step+2 
	{
		while(k--)//k记录头结点个数 
		{	
			for(i=0;i<lenc;i++)
			{
				ptr=NULL;ptr =strstr(A[h1],left[i]);
				delta=ptr-A[h1];//地址差(如abc中找到a,则delta=0.找b则为1……) 
				if(ptr == NULL || delta<0) continue;//找不到
				do {//入队
					strncpy(A[t1],A[h1],delta);//三行代码完成字符串复制与替换 
					strcat(A[t1],right[i]);
					strcat(A[t1],&A[h1][delta+strlen(left[i])]);
					for(j=0;j<=count2;j++)//双向都遍历到同一个点,即找到最短路径 
						if(strcmp(A[t1],B[h2+j])==0) 
						{printf("%d",step+1);return 0;} 
					t1++;count1++;
					ptr=NULL;ptr=strstr(A[h1]+delta+strlen(left[i]),left[i]);//在剩余字符串中再次查找 
					delta=ptr-A[h1];//地址差
				} while(ptr!=NULL &&  delta>=0); //判断找到与否 
			}
			h1++;//扩展下一个头结点 
		} 
		k=count2;count2=0;step++;//k记录头结点个数。 
		if(h2==0)k=1;
		//下面部分复制上面的,把A改B,把left改right之类即可。
		while(k--)
		{
			for(i=0;i<lenc;i++)
			{
				ptr=NULL;ptr =strstr(B[h2],right[i]);
				delta=ptr-B[h2];
				if(ptr == NULL || delta<0) continue;
				do {
					strncpy(B[t2],B[h2],delta);
					strcat(B[t2],left[i]);
					strcat(B[t2],&B[h2][delta+strlen(right[i])]);
					for(j=0;j<=count1;j++)
						if(strcmp(B[t2],A[h1+j])==0) {printf("%d",step+1);return 0;}//双向遍历到同一点,输出 
					t2++;count2++;ptr=NULL;
					ptr =strstr(B[h2]+delta+strlen(right[i]),right[i]);
					delta=ptr-B[h2];
				}while(ptr!=NULL && delta>=0);	
			}
			h2++;
		}
		k=count1;count1=0;step++;
	}
	printf("NO ANSWER!"); 
	return 0;	
}
看题解前用了没想过双向BFS,于是下面是单向版本

//单向BFS60分  
#include<stdio.h>  
#include<string.h>  
char A[1000000][21]={{'\0'}};  
int main(void)  
{  
    char left[1001][21]={{'\0'}},right[1001][21]={{'\0'}},target[21];//记录变换规则   
    int i,lenc=0,lenl=0,lenr=0,step=0;  
    scanf("%s%s",A[0],target);//记录起点与终点,双向BFS   
    while(scanf("%s%s",left[lenl++],right[lenr++])==2) lenc++;//lenc:变换规则的数量   
    int h1=0,t1=1; //h为头,t为尾。   
    int delta=0,k,count1=1;//k记录头结点个数,count记录头结点扩展出结点的数量  
    char *ptr;//delta记录搜索到的字符串地址,ptr为函数返回值。   
    while(h1 < t1 && step++ < 10)   
    {  
        k=count1;count1=0;  
        while(k--)//k记录头结点个数   
        {     
            for(i=0;i<lenc;i++)  
            {  
                ptr=NULL;ptr =strstr(A[h1],left[i]);  
                delta=ptr-A[h1];//地址差(如abc中找到a,则delta=0.找b则为1……)   
                if(ptr == NULL || delta<0) continue;//找不到  
                do {//入队  
                    strncpy(A[t1],A[h1],delta);//三行代码完成字符串复制与替换   
                    strcat(A[t1],right[i]);  
                    strcat(A[t1],&A[h1][delta+strlen(left[i])]);  
                    if(strcmp(A[t1],target)==0)   
                        {printf("%d",step);return 0;}   
                    t1++;count1++;  
                    ptr=NULL;ptr=strstr(A[h1]+delta+strlen(left[i]),left[i]);//在剩余字符串中再次查找   
                    delta=ptr-A[h1];//地址差  
                } while(ptr!=NULL &&  delta>=0); //判断找到与否   
            }  
            h1++;//扩展下一个头结点   
        }   
    }  
    printf("NO ANSWER!");   
    return 0;  
}  

顺便来个简单易写的DFS

//直接深搜40分  
#include<stdio.h>  
#include<string.h>  
#include<stdlib.h>  
char a[21]={'\0'};int min=10;  
char left[1001][21]={{'\0'}},right[1001][21]={{'\0'}},target[21]={'\0'};//记录变换规则   
int lenc=0,lenl=0,lenr=0;  
void dfs(char *A,int step)  
{  
    char *ptr=NULL;  
    int i;  
    if(step>min) return ;  
    if(strcmp(A,target)==0) {min=step;}  
    for(i=0;i<lenc;i++)  
    {  
        ptr =strstr(A,left[i]);  
        if(ptr==NULL ) continue;   
        int delta=ptr-A;  
        do {  
        char B[21]={'\0'};  
        strncpy(B,A,delta);  
        strcat(B,right[i]);  
        strcat(B,A+delta+strlen(left[i]));  
        dfs(B , step+1  );  
        ptr=NULL;   
        ptr=strstr(A+delta+strlen(left[i]),left[i]);   
        delta=ptr-A;  
        }  while(ptr!=NULL);  
    }  
}  
int main(void)  
{  
    scanf("%s%s",a,target);  
    while(scanf("%s%s",left[lenl++],right[lenr++])==2) lenc++;  
    dfs(a,0);  
    min==10 ?printf("NO ANSWER") :printf("%d",min);  
    return 0;  
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值