Problem:
Strange Key

Professor Tsukuba invented a mysterious jewelry box that can be opened
with a special gold key whose shape is very strange. It is composed
of gold bars joined at their ends. Each gold bar
has the same length and is placed parallel to one of the three
orthogonal axes in a three dimensional space,
i.e., x-axis, y-axis and z-axis.

The locking mechanism of the jewelry box
is truly mysterious, but the shape of the key is known.
To identify the key of the jewelry box, he gave a way to describe
its shape.

The description
indicates a list of connected paths that completely defines the shape
of the key: the gold bars of the key are arranged along the paths and
joined at their ends.
Except for the first path,
each path must start from an end point of a gold bar on a previously
defined path.
Each path is represented by a sequence of elements, each of which is one
of six symbols (+x, -x, +y, -y, +z and -z) or a positive integer.
Each symbol indicates the direction from an end point to the other end
point of a gold bar along the path. Since each gold bar is
parallel to one of the three orthogonal axes, the 6 symbols are enough to
indicate the direction. Note that a description of a path has
direction but the gold bars themselves have no direction.

An end point of a gold bar can have a label, which is a positive integer.
The labeled point may be referred to as the beginnings of other paths.
In a key description, the first occurrence of a positive integer
defines a label of a point and each subsequent occurrence of the same
positive integer indicates the beginning of a new path at the point.

An example of a key composed of 13 gold bars is depicted
in Figure 1.

The following sequence of lines

19
1 +x 1 +y +z 3 +z
3 +y -z +x +y -z -x +z 2 +z
2 +y

is a description of the key in Figure 1.
Note that newlines have the same role as space characters in the
description, so that "19 1 +x 1 +y +z 3 +z 3 +y -z +x +y -z -x +z
2 +z 2 +y" has the same meaning.

The meaning of this description is quite simple.
The first integer "19" means the number of
the following elements in this description.
Each element is one of the 6 symbols or a positive integer.

The integer "1" at the head of the second line
is a label attached to the starting point of the first path.
Without loss of generality,
it can be assumed that the starting point of the first path is the
origin, i.e., (0,0,0), and that the length of each gold bar is 1.
The next element "+x" indicates that the first gold bar
is parallel to the x-axis,
so that the other end point of the gold bar is at (1,0,0).
These two elements "1" and "+x" indicates
the first path consisting of only one gold bar.
The third element of the second line in the description is the positive
integer "1", meaning that the point with the label "1", i.e., the
origin (0,0,0) is the beginning of a new path.
The following elements "+y", "+z", "3", and "+z" indicate the second
path consisting of three gold bars. Note that this "3" is its first
occurrence so that the point with coordinates (0,1,1) is labeled "3".
The head of the third line "3" indicates
the beginning of the third path and so on.
Consequently, there are four paths by which the shape of the key in
Figure 1 is completely defined.

Note that there are various descriptions of the same key since there
are various sets of paths that cover the shape of the key.
For example,
the following sequence of lines

19
1 +x 1 +y +z 3 +y -z +x +y -z -x +z 2 +y
3 +z
2 +z

is another description of the key in Figure 1, since
the gold bars are placed in the same way.

Furthermore, the key may be turned 90-degrees around
x-axis, y-axis or z-axis several times and may be moved parallelly.
Since any combinations of rotations and parallel moves don't change
the shape of the key, a description of a rotated and moved key also
represent the same shape of the original key. For example, a
sequence

17
+y 1 +y -z +x
1 +z +y +x +z +y -x -y 2 -y
2 +z

is a description of a key in Figure 2 that represents the same key
as in Figure 1.
Indeed, they are congruent under a rotation around x-axis and
a parallel move.

Your job is to write a program to
judge whether or not the given two descriptions define the same key.

Note that paths may make a cycle.
For example, "4 +x +y -x -y" and "6 1 +x 1 +y +x -y"
are valid descriptions. However, two or more gold bars must not be placed at the same position.
For example, key descriptions
"2 +x -x"
and
"7 1 +x 1 +y +x -y -x" are invalid.

Input

An input data is a list of pairs of key descriptions followed by
a zero that indicates the end of the input.
For p pairs of key descriptions, the input is given in the
following format.

key-description1-a

key-description1-b

key-description2-a

key-description2-b

...

key-descriptionp-a

key-descriptionp-b

0

Each key description (key-description) has the following format.

n
e1 e2
...
ek
...
en

The positive integer n indicates the number of the following elements
e1, ...,
en .
They are separated by one or more space characters and/or newlines.
Each element ek is
one of the six symbols
(+x, -x, +y, -y, +z and -z)
or a positive integer.

You can assume that each label is a positive integer that is less than 51,
the number of elements in a single key description is less than 301,
and the number of characters in a line is less than 80.
You can also assume that the given key descriptions are valid and
contain at least one gold bar.

Output

The number of output lines should be equal to that of
pairs of key descriptions given in the input.
In each line, you should output one of two words "SAME",
when the two key descriptions represent the same key, and
"DIFFERENT", when they are different.
Note that the letters should be in upper case.

Sample Input

19
1 +x 1 +y +z 3 +z
3 +y -z +x +y -z -x +z 2 +z
2 +y
19
1 +x 1 +y +z 3 +y -z +x +y -z -x +z 2 +y
3 +z
2 +z
19
1 +x 1 +y +z 3 +z
3 +y -z +x +y -z -x +z 2 +y
2 +z
18
1 -y
1 +y -z +x
1 +z +y +x +z +y -x -y 2 -y
2 +z
3 +x +y +z
3 +y +z -x
0

Output for the Sample Input

SAME
SAME
DIFFERENT

Solution in Rust (judge verdict: Accepted)

use std::ops::DivAssign;
use std::ops::MulAssign;
use std::ops::SubAssign;
use std::collections::BinaryHeap;
use std::str::FromStr;
use std::collections::HashSet;
use std::collections::BTreeMap;
use std::fmt::Display;
use std::ops::Neg;
use std::ops::Div;
use std::ops::Mul;
use std::ops::Add;
use std::ops::{AddAssign, Sub};
use std::cmp::max;
use std::collections::VecDeque;
use std::cmp::min;
use std::collections::{HashMap, BTreeSet};
use std::cmp::Ordering;
use std::fmt::Debug;

fn read_line() -> String {
    let mut buffer = String::new();
    std::io::stdin().read_line(&mut buffer).expect("No Line");
    buffer.trim().to_owned()
}
fn read_lines<T: std::str::FromStr>(count: usize) -> Vec<T> {
    let mut buffer = String::new();
    let mut vec = Vec::with_capacity(count);
    for _ in 0 .. count {
        std::io::stdin().read_line(&mut buffer).expect("No Line");
        vec.push(buffer.trim().parse().ok().expect("Can't Parse"));
        buffer.clear();
    }
    vec
}
fn read_tabulate<R, T: Fn(&str)->R> (count: usize, transformer: T) -> Vec<R> {
    let mut buffer = String::new();
    let mut vec = Vec::with_capacity(count);
    for _ in 0 .. count {
        std::io::stdin().read_line(&mut buffer).expect("No Line");
        vec.push(transformer(buffer.trim()));
        buffer.clear();
    }
    vec
}
fn read_value<T: std::str::FromStr>() -> T {
    read_line().trim().parse().ok().unwrap()
}
fn read_values<T: std::str::FromStr>() -> Vec<T> {
    read_line().trim().split_whitespace().map(|x| x.parse().ok().expect("Can't Parse")).collect::<Vec<T>>()
}
macro_rules! freeze {
    ($($id:ident), *) => {
        $(let $id = $id;)*
    };
}
macro_rules! read_map {
    ($ident: ident: [$block: block; $size: expr]) => {
        let $ident = (0 .. $size).into_iter().map(|_| $block).collect::<Vec<_>>();
    };
    (mut $ident: ident: [$block: block; $size: expr]) => {
        let mut $ident = (0 .. $size).into_iter().map(|_| $block).collect::<Vec<_>>();
    };
}
macro_rules! read {
    (mut $ident: ident: [$ty:ty]) => {
        let mut $ident = read_values::<$ty>();
    };
    ($ident: ident: [$ty:ty]) => {
        let $ident = read_values::<$ty>();
    };
    (mut $ident: ident: [[$ty:ty]; $size: expr]) => {
        let mut $ident = (0 .. $size).into_iter().map(|_| read_values::<$ty>()).collect::<Vec<_>>();
    };
    ($ident: ident: [[$ty:ty]; $size: expr]) => {
        let $ident = (0 .. $size).into_iter().map(|_| read_values::<$ty>()).collect::<Vec<_>>();
    };
    (mut $ident: ident: [$ty:ty; $size:expr]) => {
        let mut $ident = read_lines::<$ty>($size);
    };
    ($ident: ident: [$ty:ty; $size:expr]) => {
        let $ident = read_lines::<$ty>($size);
    };
    ($ident: ident: [$block: block; $size: expr]) => {
        let $ident = (0 .. $size).into_iter().map(|_| $block).collect::<Vec<_>>();
    };
    (mut $ident: ident: [$block: block; $size: expr]) => {
        let mut $ident = (0 .. $size).into_iter().map(|_| $block).collect::<Vec<_>>();
    };
    ($($token: tt)*) => {
        let mut iter = read_values::<String>().into_iter();
        read_from_iter!(iter; $($token)*);
    };
}
macro_rules! read_from_iter {
    ($iter:expr; mut $ident:ident:$ty:ty, $($rest:tt)*) => {
        let mut $ident = $iter.next().unwrap().parse::<$ty>().expect("Can't Parse");
        read_from_iter!($iter; $($rest)*);
    };
    ($iter:expr; $ident:ident:$ty:ty, $($rest:tt)*) => {
        let $ident = $iter.next().unwrap().parse::<$ty>().expect("Can't Parse");
        read_from_iter!($iter; $($rest)*);
    };
    ($iter:expr; mut $ident:ident:$ty:ty) => {
        let mut $ident = $iter.next().unwrap().parse::<$ty>().expect("Can't Parse");
    };
    ($iter:expr; $ident:ident:$ty:ty) => {
        let $ident = $iter.next().unwrap().parse::<$ty>().expect("Can't Parse");
    };
    ($iter: expr; ) => {};
}
struct KeyValue<K, V> {
    key: K,
    value: V
}
impl <K: PartialOrd, V> PartialEq for KeyValue<K, V> {
    fn eq(&self, other: &Self) -> bool {
        self.key.eq(&other.key)
    }
}
impl <K: PartialOrd, V> Eq for KeyValue<K, V> {}
impl <K: PartialOrd, V> PartialOrd for KeyValue<K, V> {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.key.partial_cmp(&other.key)
    }
}
impl <K: PartialOrd, V> Ord for KeyValue<K, V> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.key.partial_cmp(&other.key).ok_or("Can't Compare").unwrap()
    }
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
struct Point {
    x: i32, y: i32, z: i32
}
impl Add for Point {
    type Output = Point;
    fn add(self, rhs: Point) -> <Self as std::ops::Add<Point>>::Output { 
        Point{x: self.x + rhs.x, y: self.y + rhs.y, z: self.z + rhs.z}    
    }
}
impl Sub for Point {
    type Output = Point;
    fn sub(self, rhs: Point) -> <Self as std::ops::Sub<Point>>::Output { 
        Point{x: self.x - rhs.x, y: self.y - rhs.y, z: self.z - rhs.z}
    }
}
impl Point {
    fn rotate_by_x(&self) -> Point {
        Point{x: self.x, y: -self.z, z: self.y}
    }
    fn rotate_by_y(&self) -> Point {
        Point{x: self.z, y: self.y, z: -self.x}
    }
    fn rotate_by_z(&self) -> Point {
        Point{x: -self.y, y: self.x, z: self.z}
    }
}
#[derive(Copy, Clone, Debug)]
enum Token {
    Label(usize),
    Diff(Point),
}
impl FromStr for Token {
    type Err = ();
    fn from_str(s: &str) -> std::result::Result<Self, <Self as std::str::FromStr>::Err> { 
        match s {
            "+x" => Ok(Token::Diff(Point{x: 1, y: 0, z: 0})),
            "-x" => Ok(Token::Diff(Point{x: -1, y: 0, z: 0})),
            "+y" => Ok(Token::Diff(Point{x: 0, y: 1, z: 0})),
            "-y" => Ok(Token::Diff(Point{x: 0, y: -1, z: 0})),
            "+z" => Ok(Token::Diff(Point{x: 0, y: 0, z: 1})),
            "-z" => Ok(Token::Diff(Point{x: 0, y: 0, z: -1})),
            _ => if let Ok(label) = s.parse::<usize>() {
                Ok(Token::Label(label))
            }else {
                Err(())
            }
        }
     }
}

struct Key {
    bars: BTreeSet<(Point, Point)>,
    labeled: BTreeMap<usize, Point>,
    last: Point
}
impl Key {
    fn new() -> Key {
        Key{bars: BTreeSet::new(), labeled: BTreeMap::new(), last: Point{x: 0, y: 0, z: 0}}
    }
    fn add_bar(&mut self, token: Token) {
        match token {
            Token::Label(label) => {
                if let &Some(&last) = &self.labeled.get(&label) {
                    self.last = last;
                }else {
                    self.labeled.insert(label, self.last);
                }
            },
            Token::Diff(diff) => {
                let next = self.last + diff;
                self.bars.insert((min(self.last, next), max(self.last, next)));
                self.last = next
            }
        }
    }
    fn is_same(&self, other: &Key) -> bool {
        let self_suspects = self.suspects();
        let other_normalized = Key::normalize(&other.bars.iter().cloned().collect());
        self_suspects.into_iter().any(|bar| bar.len() == other_normalized.len() && (0 .. bar.len()).all(|i| bar[i] == other_normalized[i]))
    }
    fn suspects(&self) -> Vec<Vec<(Point, Point)>> {
        let mut bars = vec![self.bars.iter().cloned().collect::<Vec<_>>()];
        for i in 0 .. 5 {
            let rotated = if i & 1 == 0 {
                bars[i].iter().map(|&(a, b)| (a.rotate_by_x(), b.rotate_by_x())).collect::<Vec<_>>()
            }else {
                bars[i].iter().map(|&(a, b)| (a.rotate_by_z(), b.rotate_by_z())).collect::<Vec<_>>()
            };
            bars.push(rotated);
        }
        bars.into_iter().flat_map(|bar| {
            let mut ret = vec![Key::normalize(&bar)];
            for i in 0 .. 3 {
                let last = Key::normalize(&ret[i].iter().map(|&(a, b)| (a.rotate_by_y(), b.rotate_by_y())).collect());
                ret.push(last);
            }
            ret
        }).collect::<Vec<_>>()
    }
    fn normalize(bars: &Vec<(Point, Point)>) -> Vec<(Point, Point)> {
        let mut bar = bars.clone();
        let origin = bar.iter().flat_map(|&(a, b)| vec![a, b]).min().unwrap();
        for i in 0 .. bar.len() {
            let a = bar[i].0 - origin;
            let b = bar[i].1 - origin;
            bar[i] = (min(a, b), max(a, b));
        }
        bar.sort();
        bar
    }
}

fn main() {
    loop {
        let mut line1 = Vec::new();
        while line1.is_empty() {
            read!(line: [String]);
            line1 = line;
        }
        let a = line1[0].parse::<usize>().ok().unwrap();
        if a == 0 {break;}
        let mut first = line1[1..].iter().map(|s| s.parse::<Token>().ok().unwrap()).collect::<Vec<_>>();
        while first.len() < a {
            read!(tokens: [Token]);
            for t in tokens {
                first.push(t);
            }
        }
        read!(line2: [String]);
        let b = line2[0].parse::<usize>().ok().unwrap();
        let mut second = line2[1..].iter().map(|s| s.parse::<Token>().ok().unwrap()).collect::<Vec<_>>();
        while second.len() < b {
            read!(tokens: [Token]);
            for t in tokens {
                second.push(t);
            }
        }
        let mut first_key = Key::new();
        for token in first {
            first_key.add_bar(token);
        }
        let mut second_key = Key::new();
        for token in second {
            second_key.add_bar(token);
        }
        if first_key.is_same(&second_key) {
            println!("SAME");
        }else {
            println!("DIFFERENT");
        }
    }
}