zju 1004 解题报告 堆栈

题目如下:

Anagrams by Stack

Time limit: 1 Seconds   Memory limit: 32768K  
Total Submit: 5453   Accepted Submit: 2313  

How can anagrams result from sequences of stack operations? There are two sequences of stack operators which can convert TROT to TORT:

[
i i i i o o o o
i o i i o o i o
]

where i stands for Push and o stands for Pop. Your program should, given pairs of words produce sequences of stack operations which convert the first word to the second.

Input

The input will consist of several lines of input. The first line of each pair of input lines is to be considered as a source word (which does not include the end-of-line character). The second line (again, not including the end-of-line character) of each pair is a target word. The end of input is marked by end of file.

Output

For each input pair, your program should produce a sorted list of valid sequences of i and o which produce the target word from the source word. Each list should be delimited by

[
]

and the sequences should be printed in "dictionary order". Within each sequence, each i and o is followed by a single space and each sequence is terminated by a new line.

Process

A stack is a data storage and retrieval structure permitting two operations:

Push - to insert an item and
Pop - to retrieve the most recently pushed item

We will use the symbol i (in) for push and o (out) for pop operations for an initially empty stack of characters. Given an input word, some sequences of push and pop operations are valid in that every character of the word is both pushed and popped, and furthermore, no attempt is ever made to pop the empty stack. For example, if the word FOO is input, then the sequence:

i i o i o ois valid, but
i i o is not (it's too short), neither is
i i o o o i(there's an illegal pop of an empty stack)

Valid sequences yield rearrangements of the letters in an input word. For example, the input word FOO and the sequence i i o i o o produce the anagram OOF. So also would the sequence i i i o o o. You are to write a program to input pairs of words and output all the valid sequences of i and o which will produce the second member of each pair from the first.

Sample Input
madam
adamm
bahama
bahama
long
short
eric
rice
Sample Output
[
i i i i o o o i o o 
i i i i o o o o i o 
i i o i o i o i o o 
i i o i o i o o i o 
]
[
i o i i i o o i i o o o 
i o i i i o o o i o i o 
i o i o i o i i i o o o 
i o i o i o i o i o i o 
]
[
]
[
i i o i o i o o 
]

本题是跟堆栈操作相关的题目,要求通过堆栈的入栈出栈操作从输入串转换成输出串的所有操作序列。

在近期对堆栈这种数据结构的学习过程中,遇到过这样一道跟本题相关的题目。有一列火车车厢依次编号1,2,..n,一个火车道,火车道看作一个堆栈,给出火车车厢编号的输入序列以及输出序列,要求判断是否能够通过车厢进入火车道从输入序列得到输出序列。这个题目的解题思路是同时扫描输入序列和输出序列,进行出栈入栈操作,如最后输入序列和输出序列均扫描完毕并且堆栈为空,则可以从输入序列得到输出序列。具体解法如下:

假设输入序列S,输出序列T,堆栈为Stack。扫描到S[i]和T[j],0<=i<S.length,0<=j<T.length。

If(S[i]==T[j])

{

    i++;

    j++;

}

else

{

    Stack.push(S[i]);

    i++;

}

else if(Stack.top()==T[j])

{

    Stack.pop();

    j++;

}

若i == S.length && j == T.length && S.empty(),则可以从输入序列得到输出序列。

 回到本题,解法可以借鉴上面这道题目。但是存在以下不同:(1)本题要求所有的操作序列;(2)输入中可以存在重复元素。根据第一个不同点,采用递归的方式搜索所有可行的操作序列,用栈来记录输入序列中出入的元素,用队列来记录产生的操作序列;第二个不同点决定S[i]==T[j]时,不能简单地入栈出栈就完事。具体处理方式如下:

S[i] == T[j]

(1) 将S[i]到S[sourceNext[i]-1]的元素入栈,i=sourceNext[i],递归处理(i,j)

程序中我们定义了一个数组sourceNext[0..S.length-1],其中sourceNext[i](0<i<S.length)表示最小的j(i<j),S[i]==S  [j]。  若不存在这样的j,则sourceNext[i]=-1。

(2) S[i]入栈出栈,i=i+1,j=j+1,递归处理(i,j)

(3) If(S.top()==T[j])

      {S.pop();j=j+1;递归处理(i,j)}

S[i] != T[j]

(1) S[i]入栈,i=i+1,递归处理(i,j)

(2) If(S.top()==T[j])

      {S.pop();j=j+1;递归处理(i,j)}

处理S[i] == T[j]的(2) 存在一个问题,如sourceNext[i]=i+1,则(2)的处理产生的操作序列与下一层递归(i+1,j)(3)产生的操作序列重合。可以假设S[i]=a,S[i+1]=a,T[j]=a,画个示意图就会发现问题。因此,只有当SourceNext[i] != i +1时进行(2)。当i=S.length && j == T.length && S.empty()时,队列记录了一个完整的操作序列。此外,题目要求有序输出操作序列。直观方法时用一个表来记录所有的操作序列,然后排序输出。但是在问题规模很大时,这几乎不可能实现。因此,我们需要在处理过程中,就要保证当前产生的操作序列是有序的。回过头来看,假设当前生成的操作序列是有序的,当S[i] == T[j]时,(1)产生的是若干个i,(2)产生i o,(3)产生o,因此为保证加入操作符号后新产生的序列也是有序的,S[i] == T[j]时处理的先后顺序应该是(1)(2)(3)。S[i] != T[j]时同理。这样最终产生的完整的操作序列均是有序的。

 

解答的源代码如下:

 #include <iostream>
#include <stack>
#include <deque>
#include <map>
#include <string>
using namespace std;

#include <string.h>
#define MAXLETTERS 1024
static int L[MAXLETTERS];
struct sourceElem
{
 char c;
 int next;
};
static string source;
static int *sourceNext;
static string target;
static stack<char> S;
static deque<char> Q;
#define PUSH 'i'
#define POP 'o'

void f(int i, int j)
{
 if(i < 0 || j < 0)
 {
  return;
 }
 //source[i]<->target[j]
 if(source[i] == target[j])
 {
  if(source[i] != 0)
  {
   
   int next = sourceNext[i];
   
   int k;
   for(k = i;k<next;k++)
   {
    S.push(source[k]);
    Q.push_back(PUSH);
   }
   f(next,j);
   
   for(k = i;k<next;k++)
   {
    S.pop();
    Q.pop_back();
   }
   if(next != i+1)
   {
    Q.push_back(PUSH);
    Q.push_back(POP);
    f(i+1,j+1);
    Q.pop_back();
    Q.pop_back();
   }
      if(j < i && target[j] != 0 && target[j] == S.top())
   {
    char temp = S.top();
    S.pop();
    Q.push_back(POP);
    f(i,j+1);
    S.push(temp);
    Q.pop_back();
   }
  }
  else if(S.top() == 0)
  {
   //successful
   int start = 0;
   int oriStrPos = 1;
   for(int k = 0;k<Q.size();k++)
   {
    cout<<Q[k]<<" ";
   }
   if(Q.size()>0)
    cout<<endl;
  }
 }
 else
 {
  if(source[i] != 0)
  {
   S.push(source[i]);
   Q.push_back(PUSH);
   f(i+1,j);
   S.pop();
   Q.pop_back();
  }
  if(j < i && target[j] != 0 && target[j] == S.top())
  {
   char temp = S.top();
   S.pop();
   Q.push_back(POP);
   f(i,j+1);
   S.push(temp);
   Q.pop_back();
  }
 }
}
void ft()
{
 int sourceLen = source.length();
 memset(L,0,MAXLETTERS);
 if(sourceLen > 0)
  sourceNext = new int[sourceLen];
 for(int i = 0;i<sourceLen;i++)
 {
  char c = source[i];
  if(L[c]!=-1)
   sourceNext[L[c]] = i;
  L[c] = i;
  sourceNext[i] = -1;
 }
}
int main(int argc,char **argv)
{
 while(cin>>source>>target)
 {
  while(!S.empty())
   S.pop();
  S.push(0);
  ft();
  cout<<"["<<endl;
  f(0,0);
  cout<<"]"<<endl;
  if(sourceNext != NULL)
  {
   delete[] sourceNext;
   sourceNext = NULL;
  }
 }
 return 0;
}
 
后来看了网上别人的解答,发现自己的解法实际上做了不少多余的工作。根本没有必要判断S[i]==T[j],我的解法中当S[i] != T[j]时的(1)(2)所做的工作实际上已经可以完成任务。处理到S[i]时,不管三七二十一,先入栈,递归处理。入栈后的元素在下面若干层递归中肯定会作为栈顶元素与T[j]比较,因此就没有必要做其它多余的判断了。

下面是修改后的代码:

#include <iostream>
#include <stack>
#include <deque>
#include <map>
#include <string>
using namespace std;

#include <string.h>

static string source;
static string target;

static stack<char> S;
static deque<char> Q;

static int l;

#define PUSH 'i'
#define POP 'o'

void f1(int i, int j)
{
 if(i == l && j == l)
 {
  for(int k = 0;k<Q.size();k++)
  {
   cout<<Q[k]<<" ";
  }
  if(Q.size()>0)
   cout<<endl;
  return;
 }

 if(i < l)
 {
  S.push(source[i]);
  Q.push_back(PUSH);
  f1(i+1,j);
  S.pop();
  Q.pop_back();
 }

 if(j < l && !S.empty() && S.top()==target[j])
 {
  char temp = S.top();
  S.pop();
  Q.push_back(POP);
  f1(i,j+1);
  S.push(temp);
  Q.pop_back();
 }
}

int main(int argc,char **argv)
{
 while(cin>>source>>target)
 {
  cout<<"["<<endl;
  int sourceLen = source.length();
  int targetLen = target.length();
  if(sourceLen == targetLen)
  {
   l = sourceLen;
   f1(0,0);
  }
  cout<<"]"<<endl;
 }

 return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值