繁体   English   中英

内存限制与python字符串比较

[英]Memory limit with python string comparison

我需要编写一个python脚本,该脚本可以匹配两个字符串列表,并返回列表中最短且按字典顺序排列的第一个公共部分。

列表彼此对应,这意味着(a1,b1)...(aN,bN)对被冻结。

规则:

a = ['are', 'you', 'how', 'alan', 'dear']
b = ['yo', 'u', 'nhoware', 'arala', 'de']

result = 'dearalanhowareyou'

如果没有这样的字符串连接,则结果为IMPOSSIBLE

a = ['a', 'b', 'c']
b = ['ab', 'bb', 'cc']

result = 'IMPOSSIBLE'

限制:

  • 每个列表中的每个元素只能使用一次
  • 每个字符串元素的长度[1; 100]
  • 列表的最大长度为11
  • 所有元素只能成对排列

现在,我尝试从短裤开始,按字典顺序首先考虑其中的所有组合和排列。

我需要提交它进行测试,在其中一项测试中,我得到了内存限制条件 ,限制为CPU 6秒和RAM 1024 MB。

我想知道如何优化内存消耗吗? 我当前的代码在这里:

from itertools import chain, combinations, groupby, permutations
import timeit
import collections
import sys
import re
import gc
from functools import reduce


def powerset(iterable):
    "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
    for c in chain(*map(lambda x: combinations(iterable, x), range(0, len(iterable)+1))):
        yield c

def give_permutation(i):
    """
    Yields a permutations of a given combination
    """
    for c in sorted(permutations(i), key = lambda x: x[0]):
        yield c

def array_loop(arr):
    """
    Generator-like loop over the list
    """
    for i, element in enumerate(arr):
        yield i, element

def create_dict(arr):
    """
    Index based dictionary
    """
    dicty = {}
    for i, v in array_loop(arr):
        dicty[i] = v
    return dicty

def tricky_sort(a1, a2):
    """
    Sorts one array and return the second array with index-wise order
    """
    for a in zip(*sorted(zip(a1, a2), key=lambda x: (x[0]))):
        yield a

def num_common_letters(a, b):
    """
    Returns number of common elements in two strings
    """
    ac = collections.Counter(a)
    bc = collections.Counter(b)
    return sum(min(ac[key], bc[key]) for key in ac)

def checkMatch(a1, a2):
    """
    Checks for the first shortest match between strings
    """

    assert len(a1) == len(a2)
    iteration_mode = 'fast' if len(a1) >= 8 or len(min(a1, key=len)) >= 10 else 'full'

    # fast check for first sorted elements are equal
    if a1[0] == a2[0]:
        return a1[0]

    # fast check for equal length
    check = 0
    for el1, el2 in zip(a1, a2):
        if len(a1) != len(a2):
            check += 1
            break
    if check == 0 and len(a1) != len(a2):
        return 'IMPOSSIBLE'
    if ''.join(a for a in a1) == ''.join(a for a in a2)[-1:]:
        return 'IMPOSSIBLE'
    # fast check if any two strings have common elements
    if num_common_letters(''.join(a for a in a1), ''.join(a for a in a2)) < 2*min([len(min(a1, key=len)), len(min(a2, key=len))]):
        return 'IMPOSSIBLE'

    lookup_a1 = create_dict(a1)
    lookup_a2 = create_dict(a2)
    range_list = list(range(len(a1)))

    del a1, a2

    clean_combs = []
    sorted_names = []


    for i in powerset(range_list):
        if len(i) > 0:
            if len(''.join(lookup_a1[index] for index in i)) == len(''.join(lookup_a2[index] for index in i)):
                if reduce(lambda x, y: x + y, sorted(''.join(lookup_a1[index] for index in i))) == \
                    reduce(lambda x, y: x + y, sorted(''.join(lookup_a2[index] for index in i))):
                    clean_combs.append(i)
                    sorted_names.append(sorted([lookup_a1[index] for index in i])[0][0])

    del range_list

    if len(clean_combs) > 0:
        _, clean_combs = tricky_sort(sorted_names, clean_combs)
        del sorted_names
        matches = []
        for i in clean_combs:
            for combination in give_permutation(i):
                if lookup_a1[combination[0]][0] != lookup_a2[combination[0]][0]:
                    continue
                first_seq = [lookup_a1[index] for index in combination]
                second_seq = [lookup_a2[index] for index in combination]
                if ''.join(f for f in first_seq) == ''.join(s for s in second_seq):
                    if iteration_mode == 'fast':
                        return ''.join(f for f in first_seq)
                    else:
                        matches.append(''.join(f for f in first_seq))

        if len(matches) > 0:
            matches = sorted(matches, key=lambda x: (len(x), x[0]))
            return matches[0]

    return 'IMPOSSIBLE'


def string_processor(string):
    """
    Splits string by integers and returns arrays with only letters inside
    """
    arr = ' '.join(re.findall(r'[0-9|a-zA-Z]+', string.replace(r'\n', ' '))).strip()
#     all_ints = re.findall(r'[0-9]+', arr)
    arr = re.compile(r'[0-9]+').split(arr)

#     flatten = lambda l: [item for sublist in l for item in sublist]
    arr = [re.findall(r'[a-zA-Z]+', a) for a in arr if len(a) > 0]

#     assert sum([int(a) for a in all_ints]) == len(flatten(result_list)) / 2
#     assert min([len(f) for f in flatten(result_list)]) > 0
#     assert len(flatten(result_list)) < 11*sum([int(a) for a in all_ints])

    for r in arr:
        yield r

def substring_processor(substring, shift = 0):
    """
    Returns two array with the first and the second sequences
    """
    arr1 = []
    arr2 = []
    for i in range(0, len(substring), 2):
        yield substring[i + shift]

def string_arr(arr1, arr2):    
    for t in tricky_sort(arr1, arr2):
        yield t

def process_file(file):
    """
    Iterates over all sequences in a file
    """

    case_counter = 0

    for sub in string_processor(file):
        case_counter += 1
        str1, str2 = string_arr(substring_processor(sub), substring_processor(sub, shift = 1))
        print('Case %s: ' %  str(case_counter) + checkMatch(str1, str2) + '\n')

def read_files():
    """
    Takes input data
    """
    input_string = ''
    for f in sys.stdin:
        input_string += f
    process_file(input_string)

read_files()

这个问题在C ++中解决了,但我无法理解这个主意https://github.com/adrian-budau/work/blob/master/Kattis/ACM-ICPC%20-%20World%20Finals%202013/Limited% 20Correspondence / main.cpp中

我认为您可以生成正确的子字符串(在我看来,所有这些子字符串在生成时间中,您都无法真正确定它们是否既是“字典上的” 又是最短的),如下所示:

from itertools import permutations
from pprint import pprint
a = ['are', 'you', 'how', 'alan', 'dear']
b = ['yo', 'u', 'nhoware', 'arala', 'de']
c = zip(a,b)
m = []
for p in permutations(c):
  stra = ""
  strb = ""
  for t in p:
    stra += t[0]
    strb += t[1]
    if stra == strb: m.append(stra)
pprint(m)

然后继续检查m仍然为空,或者以任何笨拙的方式选择“第一”项目,因为该列表反而会很短。 就像按字母顺序对它进行排序,然后选择最排序的一个:

if len(m) == 0: w = "IMPOSSIBLE"
else:
  w = m[0]
  for x in sorted(m):
    if len(x) < len(w): w = x
print(w)

因此,我最终明确地指出了无法进行此类组合的情况。 例如,我们知道答案字符串应以相同的符号开头和结尾。 因此,我们可以创建组合,并在进行排列之前检查此类条件。 我们还可以检查需要检查的片段中所有子字符串的长度总和是否相等。 如果不是,那么我们不进行置换,而是继续进行

from itertools import chain, combinations, groupby, permutations
import timeit
import collections
import sys
import re
import gc
from functools import reduce


def powerset(iterable):
    "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
    for c in chain(*map(lambda x: combinations(iterable, x), range(0, len(iterable)+1))):
        yield c

def give_permutation(i):
    """
    Yields a permutations of a given combination
    """
    for c in permutations(i):
        yield c

def create_dict(arr):
    """
    Index based dictionary
    """
    dicty = {}
    for i, v in enumerate(arr):
        dicty[i] = v
    return dicty

def tricky_sort(a1, a2):
    """
    Sorts one array and return the second array with index-wise order
    """
    for a in zip(*sorted(zip(a1, a2), key=lambda x: (len(x), x[0]))):
        yield a

def check_complete(starts, ends, array, threshold = 2):
    """
    Checks if combination has both starts and ends
    """
    matches = 0
    for s in starts: 
        if s in array:
            matches += 1
            break
    for e in ends:
        if e in array:
            matches += 1
            break
    return matches == abs(threshold)

def check_match(pairs):
    """
    Checks if answer fits the task
    """
    return ''.join(el[0] for el in pairs) == ''.join(el[1] for el in pairs) 

def algo(a1, a2):
    """
    Checks for the first shortest match between strings
    """

    assert len(a1) == len(a2)

    # fast check for first sorted elements are equal
    if a1[0] == a2[0]:
        return a1[0]

    start_pairs = []
    end_pairs = []
    matches = [] 
    all_pairs = []
    for el1, el2 in zip(a1, a2):
        if el1[0] == el2[0]: start_pairs.append((el1, el2))
        if el1[-1] == el2[-1]: end_pairs.append((el1, el2))
        if el1 == el2: matches.append(el1)
        all_pairs.append(((el1, el2)))
    if len(start_pairs) == 0 or len(end_pairs) == 0: return 'IMPOSSIBLE'

    full_search = 2

    if len(start_pairs) == 1 and len(end_pairs) == 1: 
        full_search = 0
        all_pairs.remove(start_pairs[0])
        all_pairs.remove(end_pairs[0])

    if full_search == 0: 
        if start_pairs[0] == end_pairs[0]:
            return start_pairs[0][0]
        elif start_pairs[0][0] + end_pairs[0][0] == start_pairs[0][1] + end_pairs[0][1]:
            matches.append(start_pairs[0][0] + end_pairs[0][0])
        elif end_pairs[0][0] + start_pairs[0][0] == end_pairs[0][1] + start_pairs[0][1]:
             matches.append(end_pairs[0][0] + start_pairs[0][0])

    lookup_a1 = create_dict([el[0] for el in all_pairs])
    lookup_a2 = create_dict([el[1] for el in all_pairs])
    range_list = list(range(len(all_pairs)))

    del a1, a2

    clean_combs = []
    sorted_names = []

    if len(range_list) > 0:
        for i in powerset(range_list):
            if len(i) > 0 :
                if full_search == 2:
                    if check_complete(start_pairs, end_pairs, [all_pairs[index] for index in i]) and \
                            sum([len(all_pairs[index][0]) for index in i]) == sum([len(all_pairs[index][1]) for index in i]):
                        arr1_str = ''.join(lookup_a1[index] for index in i)
                        arr2_str = ''.join(lookup_a2[index] for index in i)
                        if len(arr1_str) == len(arr2_str):
                            if reduce(lambda x, y: x + y, sorted(arr1_str)) == reduce(lambda x, y: x + y, sorted(arr2_str)):
                                clean_combs.append(i)
                else:
                    clean_combs.append(i)

        if len(clean_combs) > 0:
            for i in clean_combs:
                for combination in give_permutation(i):
                    if full_search == 2:
                        if lookup_a1[combination[0]][0] != lookup_a2[combination[0]][0] or \
                            lookup_a1[combination[-1]][-1] != lookup_a2[combination[-1]][-1]:
                                continue
                        if check_match([all_pairs[index] for index in combination]):
                            matches.append(''.join(all_pairs[index][0] for index in combination))
                    elif full_search == 0:
                        option = start_pairs + [all_pairs[index] for index in combination] + end_pairs
                        if check_match(option):
                            matches.append(''.join(el[0] for el in option))

    if len(matches) > 0:
        matches = sorted(matches, key=lambda x: (len(x), x[0]))
        return matches[0]    
    return 'IMPOSSIBLE'

def string_processor(string):
    """
    Splits string by integers and returns arrays with only letters inside
    """
    arr = ' '.join(re.findall(r'[0-9|a-zA-Z]+', string.replace(r'\n', ' '))).strip()
    all_ints = re.findall(r'[0-9]+', arr)
    arr = re.compile(r'[0-9]+').split(arr)
    arr = [re.findall(r'[a-zA-Z]+', a) for a in arr if len(a) > 0]

    for r in arr:
        yield r

def substring_processor(substring, shift = 0):
    """
    Returns two array with the first and the second sequences
    """
    arr1 = []
    arr2 = []
    for i in range(0, len(substring), 2):
        yield substring[i + shift]

def string_arr(arr1, arr2):    
    for t in tricky_sort(arr1, arr2):
        yield t

def process_file(file):
    """
    Iterates over all sequences in a file
    """

    case_counter = 0

    for sub in string_processor(file):
        case_counter += 1
        str1, str2 = string_arr(substring_processor(sub), substring_processor(sub, shift = 1))
        print('Case %s: ' %  str(case_counter) + algo(str1, str2) + '\n')

def read_files():
    """
    Takes input data
    """
    input_string = ''
    for f in sys.stdin:
        input_string += f
    process_file(input_string)

read_files()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM