洛谷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;
}