use std::ascii::AsciiExt;
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: String) => {
        let mut $ident = read_value::<String>();
    };
    ($ident: ident: String) => {
        let $ident = read_value::<String>();
    };
    (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(Clone, Debug)]
struct Hint<'a> {
    digits: &'a Vec<u32>,
    hit: usize, blow: usize, current: usize
}
impl <'a> Hint<'a> {
    fn next_state(&self, c: u32) -> Option<Hint<'a>> {
        if self.digits[self.current] == c {
            if self.hit == 0 {
                None
            }else {
                Some(Hint{digits: self.digits, hit: self.hit - 1, blow: self.blow, current: self.current + 1})
            }
        }else if self.digits.iter().any(|&d| d == c) {
            if self.blow == 0 {
                None
            }else {
                Some(Hint{digits: self.digits, hit: self.hit, blow: self.blow - 1, current: self.current + 1})
            }
        }else {
            if self.hit + self.blow + self.current >= self.digits.len() {
                None
            }else {
                Some(Hint{digits: self.digits, hit: self.hit, blow: self.blow, current: self.current + 1})
            }
        }
    }
}
fn dfs<'a>(current: usize, digits: &mut Vec<u32>, max_digit: usize, hints: &Vec<Hint<'a>>, used: &mut Vec<bool>) -> Result<Option<Vec<u32>>, ()> {
    if current == max_digit {
        return Ok(Some(digits.clone()))
    }
    let mut result = None;
    for i in 0 .. used.len() {
        if used[i] {continue;}
        let next_hints = hints.iter().flat_map(|h| h.next_state(i as u32)).collect::<Vec<_>>();
        if next_hints.len() != hints.len() {continue;}
        used[i] = true;
        digits.push(i as u32);
        match dfs(current + 1, digits, max_digit, &next_hints, used) {
            Ok(None) => (),
            Ok(ref r) if r.is_some() && result.is_none() => result = r.clone(),
            _ => return Err(())
        }
        digits.pop();
        used[i] = false;
    }
    Ok(result)
} 

fn main() {
    loop {
        read!(l: usize, h: usize);
        if l == 0 && h == 0 {break;}
        read_map!(lines: [{
            read!(digits: String, hit: usize, blow: usize);
            (digits.chars().map(|d| d.to_digit(10).unwrap()).collect::<Vec<_>>(), hit, blow)
        }; h]);
        let hints = lines.iter().map(|t| Hint{digits: &t.0, hit: t.1, blow: t.2, current: 0}).collect::<Vec<_>>();
        let mut used = vec![false; 10];
        let mut digits = Vec::with_capacity(l);
        match dfs(0, &mut digits, l, &hints, &mut used) {
            Ok(Some(r)) => println!("{}", r.into_iter().map(|d| d.to_string()).collect::<Vec<_>>().join("")),
            _ => println!("NO")
        }
    }
}


Measured memory_bytes: 5056